12

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

4 回答 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
于 2013-03-01T05:26:53.127 回答
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
于 2013-03-01T05:33:00.120 回答
6

isMidPoint0如果有,下面将识别中间。

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 & !v[-1]在元素及其后继为 0 的任何地方都将为 TRUE。添加,& !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]
于 2013-03-01T05:45:12.167 回答
1

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

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
于 2019-06-26T18:05:16.743 回答