I have a logical vector:
x <- c(FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)
How can I find the indices of the FALSE
values that are between the TRUE
values?
In this example, I am referring to the 7,8,9
indices.
CodePudding user response:
Vectorized one-liner:
which(cumsum(x) & rev(cumsum(rev(x))) & !x)
x <- c(FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)
which(cumsum(x) & rev(cumsum(rev(x))) & !x)
#> [1] 7 8 9
Works when the FALSE
positions are non-contiguous:
x <- c(TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE)
which(cumsum(x) & rev(cumsum(rev(x))) & !x)
#> [1] 2 6
For kicks, benchmarking the best answers provided so far (plus a few extra):
library(spatstat.utils)
x <- sample(!0:1, 1e5, TRUE)
f0 <- function(x) which(!x[(i <- which.max(x)):(length(x) - which.max(rev(x)) 1L)]) i - 1L
f1 <- function(x) which(!x[(i <- match(TRUE, x)):(length(x) - match(TRUE, rev(x)) 1L)]) i - 1L
f2 <- function(x) which(!x[((r <- range(which(x)))[1] 1L):(r[2] - 1L)]) r[1]
f3 <- function(x) (i <- which(!x))[i > (r <- range(which(x)))[1] & i < r[2]]
f4 <- function(x) which((!x)*(cs <- cumsum(x))*(cs < cs[length(x)]) > 0L)
f5 <- function(x) which((cs <- cumsum(x)) & (cs < cs[length(x)]) & !x)
f6 <- function(x) which(!x[(i <- which.max(x)):max.col(t(x), "last")]) i - 1L
f7 <- function(x) sequence((i <- diff(which(x))) - 1L, cumsum(i) - i which.max(x) 1L)
f8 <- function(x) which(!x & replace((cs <- cumsum(x)) > 0L, 1L, FALSE) & cs != tail(cs, 1L))
f9 <- function(X) which(cumsum(x) & rev(cumsum(rev(x))) & !x)
f10 <- function(x) which(cumsum(x) & revcumsum(x) & !x)
f11 <- function(x) which(!(x | (i <- cumsum(c(1L, diff(x) != 0L))) %in% range(i)))
microbenchmark::microbenchmark(
f0 = f0(x),
f1 = f1(x),
f2 = f2(x),
f3 = f3(x),
f4 = f4(x),
f5 = f5(x),
f6 = f6(x),
f7 = f7(x),
f8 = f8(x),
f9 = f9(x),
f10 = f10(x),
f11 = f11(x),
times = 1e3,
check = "identical"
)
#> Unit: microseconds
#> expr min lq mean median uq max neval
#> f0 756.3 933.80 1336.285 1045.20 1098.00 37121.1 1000
#> f1 789.7 965.85 1582.188 1166.95 1234.80 36911.8 1000
#> f2 976.6 1161.05 1454.476 1272.10 1331.30 15566.3 1000
#> f3 996.3 1211.75 1613.410 1385.25 1461.75 36375.5 1000
#> f4 1052.6 1324.25 1640.345 1390.25 1472.05 12286.4 1000
#> f5 1137.8 1358.00 1920.209 1530.00 1632.05 17655.6 1000
#> f6 1198.2 1417.60 1742.862 1533.35 1606.90 7776.7 1000
#> f7 1167.2 1336.50 1703.743 1564.70 1631.95 12669.5 1000
#> f8 1505.3 1784.00 2191.487 1915.05 2024.70 37308.7 1000
#> f9 1581.5 2095.40 2778.765 2244.95 2389.50 42075.5 1000
#> f10 1753.1 2188.40 2735.663 2330.70 2500.00 15522.6 1000
#> f11 2869.2 3531.75 4284.547 3785.70 4095.50 33816.4 1000
CodePudding user response:
Would this work?
tmp = cumsum(x)
prev_true = replace(tmp > 0L, 1L, FALSE)
subs_true = (tmp != tail(tmp, 1L))
which(!x & prev_true & subs_true)
# [1] 7 8 9
CodePudding user response:
You could try
grp <- cumsum(c(1, diff(x) != 0))
which(!(x | grp %in% range(grp)))
# [1] 7 8 9
CodePudding user response:
Probably you can try cut
which
like below
> (idx <- which(!x))[!is.na(cut(idx, which(x)))]
[1] 7 8 9
or a faster version with which
range
idx <- which(!x)
r <- range(which(x))
idx[idx > r[1] & idx < r[2]]