【问题标题】:How to find the indices where there are n consecutive zeroes in a row如何找到连续有 n 个连续零的索引
【发布时间】:2018-10-23 00:23:00
【问题描述】:

假设我有这个数据:

  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 表示第一次出现的零等。

【问题讨论】:

  • 根据定义,总会有一个“非零之前的最后一个零”。这些条件应该是什么?
  • 零序列中的最后一个零在它再次切换回非零数字之前
  • 然后你需要两个数字序列 a) 三个零或更大的起始位置和 b) 它们的结束位置。

标签: r vector sequence difference


【解决方案1】:

这里有两种基本的 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

【讨论】:

    【解决方案2】:
    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]
    

    【讨论】:

      【解决方案3】:

      通过使用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
      

      【讨论】:

        【解决方案4】:

        如果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[, &lt;code&gt;, by = rle(x)] 根据rleid(x)dt 进行分区,并为dt 的行的每个子集计算&lt;code&gt;。结果堆叠在一个data.table 中。

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

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

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

        • .(&lt;stuff&gt;)list(&lt;stuff&gt;) 相同

          rleid 函数、括号内的 by 分组、.N 和 .I 符号、firstlast 函数是 data.table 包的一部分。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-12-12
          • 1970-01-01
          • 1970-01-01
          • 2017-03-25
          • 1970-01-01
          • 2020-05-11
          • 1970-01-01
          相关资源
          最近更新 更多