在R中查找连续的零序列

12

我是一个有用的助手,可以翻译编程相关内容。以下是需要翻译的内容:

我有一个非常大的数据框(实际上是一个数据表)。现在,为了简化事情,让我们假设我的数据框如下:

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值)所在的行数。因此,在上面的示例中,第一行在最后一个序列中有三个连续的零,但第二行没有。
如果只有一个向量(而不是数据框),我知道如何做到这一点:
runs <-  rle(x[is.na(x)==F])

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

我显然可以使用循环并得到我想要的结果。但这将非常低效,而且我的实际数据框相当大。那么,有没有什么方法可以更快地完成任务?
我猜apply可能有用,但我现在无法想到如何使用它。另外,也许有一种使用data.table的方法?
附注:实际上,这个数据框是我原始数据表重塑后的版本。如果以某种方式可以用原始格式的数据框完成工作,那就没问题了。要查看我的原始数据框的样子,只需将其视为:
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))))

2
在数据表中跨多列操作会导致效率低下,因此您的原始宽数据框对于您的示例并不特别有用。 - mnel
4个回答

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

8
这是一个基于您所提供的向量解决方案的应用语句。它可能会达到您的要求。
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

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,这使得评估变得非常简单(假设没有 NAs)。

如果 v 是您的值(没有 NAs),那么 !v & !v[-1] 将在任何一个元素及其后继者都是 0 的地方为 TRUE。再加上 & !v[-(1:2)],这将在任何连续三个 0s 的中间为真。请注意,这也会捕捉到连续四个以上的 0s

然后,所要做的就是(1)在删除(并考虑!)任何 NAs 的情况下计算上述内容,以及(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”。如果这些很重要,您应该使用@mnel的方法。 - Ricardo Saporta
你的回答很有用,但我只需要找出最后一个序列是否为TRUE。 - Manoel Galdino
2
@ManoelGaldino,你可以使用max(which(isMidpoint))来告诉你最后一个为真的序列。 - Ricardo Saporta

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接