在R中找到连续的零序列

时间:2013-03-01 04:56:09

标签: r data.table

我有一个非常大的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.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))))

4 个答案:

答案 0 :(得分:20)

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

答案 1 :(得分:8)

这是一个基于您的矢量解决方案的apply语句。它可能会做你想要的。

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

答案 2 :(得分:6)

下面的

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]

说明:

要查找连续三个系列,您只需要进行比较 从第二个到第二个到第二个的每个元素与它之前和之后的邻居。

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

如果!v & !v[-1]是您的值(没有NAs),则& !v[-(1:2)]在任何地方都为TRUE 元素及其后继者为0的位置。加入0s,这样就可以了 只要你有一系列三个0s的中间部分,就应该是真实的。 请注意,这也会捕获一系列4 + data.table

然后剩下的就是(1)计算上述同时去除(和计算!)任何NA,以及(2)按id值分离。幸运的是, > 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]

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

{{1}}

答案 3 :(得分:1)

基于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 & rle_len >= 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