Home > Enterprise >  How can I find the indices of the FALSE values that are between the TRUE values?
How can I find the indices of the FALSE values that are between the TRUE values?

Time:08-18

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]]
  • Related