【问题标题】:Find consecutive sequence of zeros in R在R中查找连续的零序列
【发布时间】:2013-03-01 04:56:08
【问题描述】:

我有一个非常大的 data.frame(实际上是一个 data.table)。现在,为了简化事情,假设我的 data.frame 如下:

x <- c(1, 1, 0, 0, 1, 0, 0, NA, NA, 0) 
y <- c(1 ,0 ,NA, NA, 0, 0, 0, 1, 1, 0)
mydf <- data.frame(rbind(x,y))

我想确定最后一个序列在哪一行(如果有)由三个连续的零组成,不考虑 NA。因此,在上面的示例中,第一行在最后一个序列中有三个连续的零,但第二个没有。

只要我有一个向量(不是 data.frame),我就知道该怎么做:

runs <-  rle(x[is.na(x)==F])

runs$lengths[length(runs$lengths)] > 2 & runs$values[length(runs$lengths)]==0

我显然可以做一个循环,我会得到我想要的。但这会非常低效,而且我的实际 data.frame 非常大。那么,关于如何以最快的方式完成的任何想法?

我想 apply 可能有用,但我现在无法考虑使用它。另外,也许有一种 data.table 方法可以做到这一点?

ps.:实际上,这个 data.frame 是我原来的 data.table 的改版版本。如果我能以某种方式使用原始格式的 data.frame 完成这项工作,那没关系。要查看我的 data.frame 最初如何,只需将其视为:

x <- c(1, 1, 0, 0, 1, 0, 0, 0) 
y <- c(1 ,0 , 0, 0, 0, 1, 1, 0)

myOriginalDf <- data.frame(value=c(x,y), id=rep(c('x','y'), c(length(x), length(y))))

【问题讨论】:

  • 处理 data.table 中的多个列效率会很低,因此您的原始宽 data.frame 对您的示例不是特别有用

标签: r data.table


【解决方案1】:

使用data.table,正如您的问题所暗示的那样,您实际上想要这样做,据我所知,这正在做您想做的事情

DT <- data.table(myOriginalDf)

# add the original order, so you can't lose it
DT[, orig := .I]

# rle by id, saving the length as a new variables

DT[, rleLength := {rr <- rle(value); rep(rr$length, rr$length)}, by = 'id']

# key by value and length to subset 

setkey(DT, value, rleLength)

# which rows are value = 0 and length > 2

DT[list(0, unique(rleLength[rleLength>2])),nomatch=0]

##    value rleLength id orig
## 1:     0         3  x    6
## 2:     0         3  x    7
## 3:     0         3  x    8
## 4:     0         4  y   10
## 5:     0         4  y   11
## 6:     0         4  y   12
## 7:     0         4  y   13

【讨论】:

    【解决方案2】:

    这是一个基于您的向量解决方案的应用语句。它可能会做你想做的事。

    z <- apply(mydf,1, function(x) {
    runs <-  rle(x[is.na(x)==FALSE]) ;
    runs$lengths[length(runs$lengths)] > 2 & runs$values[length(runs$lengths)]==0 })
    
    mydf[z,]
    
    #   X1 X2 X3 X4 X5 X6 X7 X8 X9 X10
    # x  1  1  0  0  1  0  0 NA NA   0
    

    【讨论】:

      【解决方案3】:

      下面的isMidPoint 将标识中间的0(如果有的话)。

      library(data.table)
      myOriginalDf <- data.table(myOriginalDf, key="id")
      
      myOriginalDf[, isMidPoint := FALSE]
      myOriginalDf <- myOriginalDf[!is.na(value)][(c(FALSE, !value[-(1:2)], FALSE) & c(!value[-(length(value))], FALSE) & c(FALSE, !value[-length(value)])), isMidPoint := TRUE, by=id]
      

      解释:

      要连续查找三个系列,您只需比较 从第 2 个到倒数第 2 个的每个元素及其之前和之后的邻居。

      由于您的值是 0 / 1,因此它们实际上是 T / F,而这 使得评估变得非常简单(假设没有 NA)。

      如果 v 是您的值(没有 NA),那么 !v &amp; !v[-1] 在任何地方都是 TRUE 其中一个元素及其后继元素为 0。添加 &amp; !v[-(1:2)] 这将 无论您拥有一系列三个0s 的中间位置,都是真实的。 请注意,这也捕获了一系列 4+ 0s

      剩下的就是 (1) 计算上述内容,同时删除(并考虑!)任何 NA,以及 (2) 按 id 值分隔。幸运的是,data.table 让这些变得轻而易举。

      结果:

        > myOriginalDf
      
          row value id isMidPoint
       1:   1     1  x      FALSE
       2:   2     1  x      FALSE
       3:   3     0  x      FALSE
       4:   4     0  x      FALSE
       5:   5     1  x      FALSE
       6:   6     0  x      FALSE
       7:   7     0  x       TRUE  <~~~~
       8:   9     0  x      FALSE
       9:  10     1  x      FALSE
      10:  11     0  x      FALSE
      11:  12     0  x       TRUE  <~~~~
      12:  13     0  x       TRUE  <~~~~
      13:  14     0  x       TRUE  <~~~~
      14:  15     0  x      FALSE
      15:  16     1  y      FALSE
      16:  17     0  y      FALSE
      17:  18     0  y       TRUE  <~~~~
      18:  20     0  y      FALSE
      19:  21     1  y      FALSE
      20:  22     1  y      FALSE
      21:  23     0  y      FALSE
      22:  25     0  y       TRUE  <~~~~
      23:  27     0  y       TRUE  <~~~~
      24:  29     0  y      FALSE
          row value id isMidPoint
      

      根据评论进行编辑:

      如果您想找到真正使用的最后一个序列:

          max(which(myOriginalDf$isMidpoint))
      

      如果你想知道最后一个序列是否正确,请使用:

        # Will be TRUE if last possible sequence is 0-0-0
        #   Note, this accounts for NA's as well
        myOriginalDf[!is.na(value), isMidpoint[length(isMidpoint)-1]
      

      【讨论】:

      • 注意:我的方法无法保留NA's。如果这些很重要,您可能需要使用 @mnel 的方法。
      • 你的答案很有用,但我只需要查找最后一个序列是否为TRUE。
      • @ManoelGaldino,您可以使用max(which(isMidpoint)) 告诉您最后一个正确的序列。
      【解决方案4】:

      基于rle 的Base R 解决方案,将每个长度计数重复多次:

      rle_lens <- rle(myOriginalDf$value)$lengths
      myOriginalDf$rle_len <- unlist(lapply(1:length(rle_lens), function(i) rep(rle_lens[i], rle_lens[i])))
      

      然后你可以对其中的行进行子集value == 0 &amp; rle_len &gt;= 3(可选地将行号保留为新列)

      > myOriginalDf
         value id rle_len
      1      1  x       2
      2      1  x       2
      3      0  x       2
      4      0  x       2
      5      1  x       1
      6      0  x       3
      7      0  x       3
      8      0  x       3
      9      1  y       1
      10     0  y       4
      11     0  y       4
      12     0  y       4
      13     0  y       4
      14     1  y       2
      15     1  y       2
      16     0  y       1
      

      【讨论】:

        猜你喜欢
        • 2014-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-11
        • 1970-01-01
        相关资源
        最近更新 更多