如何找到一行中有 n 个连续零的索引

How to find the indices where there are n consecutive zeroes in a row

假设我有这个数据:

  x = c(14,14, 6,  7 ,14 , 0 ,0  ,0 , 0,  0,  0 , 0 , 0,  0 , 0 , 0 , 0,  9  ,1 , 3  ,8  ,9 ,15,  9 , 8, 13,  8,  4 , 6 , 7 ,10 ,13,  3,
 0 , 0 , 0 , 0 , 0 , 0,  0,  0 , 0 , 0 , 0,  0,  0,  0,  0  ,0,  0 , 0 , 0,  0,  0,  0,  0 , 0,  0, 4 , 7  ,4,  5 ,16 , 5  ,5 , 9 , 4  ,4,  9 , 8,  2,  0  ,0  ,0  ,0  ,0,  0,  0,  0  ,0 , 0,  0,  0,  0,  0,  0,  0,  0,0)

x
 [1] 14 14  6  7 14  0  0  0  0  0  0  0  0  0  0  0  0  9  1  3  8  9 15  9  8
[26] 13  8  4  6  7 10 13  3  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
[51]  0  0  0  0  0  0  0  0  4  7  4  5 16  5  5  9  4  4  9  8  2  0  0  0  0
[76]  0  0  0  0  0  0  0  0  0  0  0  0  0  0  

我想恢复从连续 3 个以上的零开始到非零之前的最后一个 0 结束的索引。

例如,

我会得到

6、17 表示第一个零等

Starts = which(diff(x == 0) == 1) + 1
Ends   = which(diff(x == 0) == -1)
if(length(Ends) < length(Starts)) {
    Ends = c(Ends, length(x)) }

Starts
[1]  6 34 72
Ends
[1] 17 58 89

这适用于您的测试数据,但允许 任何 个零序列,包括短零。为确保您获得长度至少为 n 的序列,请添加:

n=3
Long = which((Ends - Starts) >= n)
Starts = Starts[Long]
Ends = Ends[Long]

这里有两种基本的 R 方法:

1) rle 先运行 rle 再计算ok 挑出长度大于3的零序列.然后我们计算所有重复序列的 startsends 子集到最后的 ok 个。

with(rle(x), {
  ok <- values == 0 & lengths > 3
  ends <- cumsum(lengths)
  starts <- ends - lengths + 1
  data.frame(starts, ends)[ok, ]
})

给予:

  starts ends
1      6   17
2     34   58
3     72   89

2) gregexpr 取每个数字的符号——即 0 或 1,然后将它们连接成一个长字符串。然后使用 gregexpr 找到至少 4 个零的位置。结果给出了起点,终点可以从该结果加上 match.length 属性减去 1 来计算。

s <- paste(sign(x), collapse = "")
g <- gregexpr("0{4,}", s)[[1]]
data.frame(starts = 0, ends = attr(g, "match.length") - 1) + g

给予:

  starts ends
1      6   17
2     34   58
3     72   89

通过使用 dplyr ,得到 diff 然后如果 diff 不等于 0 ,它们不属于同一组,在 cumsum 之后我们得到 grouid

library(dplyr)
df=data.frame('x'=x,rownumber=seq(length(x)))
df$Groupid=cumsum(c(0,diff(df$x==0))!=0)
df%>%group_by(Groupid)%>%summarize(start=first(rownumber),end=last(rownumber),number=first(x),size=n())%>%filter(number==0&size>=3)
# A tibble: 3 x 5
  Groupid start   end number  size
    <int> <int> <int>  <dbl> <int>
1       1     6    17      0    12
2       3    34    58      0    25
3       5    72    89      0    18

如果x恰好是data.table的一列,你可以

library(data.table)
dt <- data.table(x = x)

dt[, if(.N > 3 & all(x == 0)) .(starts = first(.I), ends = last(.I))
   , by = rleid(x)]

#    rleid starts ends
# 1:     5      6   17
# 2:    22     34   58
# 3:    34     72   89

解释:

  • rleid(x)x 中的每个元素给出一个 ID(整数)表示 元素是哪个“运行”的成员,其中“运行”表示序列 相邻的相等值。

  • dt[, <code>, by = rle(x)] 根据 rleid(x)dt 进行分区,并为 dt 的行的每个子集计算 <code>。结果叠加在一个 data.table.

  • .N是给定子集中的元素个数

  • .I是子集对应的行号向量

  • firstlast 给出向量的第一个和最后一个元素

  • .(<stuff>) 等同于 list(<stuff>)

    rleid函数,by括号内的分组,.N和.I符号,firstlast函数是[=12=的一部分] 包。