根据某列上的rep函数查找R中行中的序列

时间:2014-07-27 14:36:17

标签: r data.table


我试图根据某列的rep函数连续找到一个0的序列。下面是我迄今为止最好的尝试,它会引发错误。我尝试使用一个应用循环,但失败了,我真的不想使用for循环,除非我必须,因为我的真实数据集是大约800,000行。我试过查找解决方案,但似乎找不到任何东西,花了几个小时在这里,没有运气。我还附上了所需的输出。

library(data.table)

TEST_DF <- data.table(INDEX = c(1,2,3,4),
                      COL_1 = c(0,0,0,0),
                      COL_2 = c(0,0,2,5),
                      COL_3 = c(0,0,0,0),
                      COL_4 = c(0,2,0,1),
                      DAYS  = c(4,4,2,2))

IN_FUN <- function(x, y)
{
  x <- rle(x)

  if( max(as.numeric(x$lengths[x$values == 0])) >= y )
  {
    "Y"
  }
  else
  {
    "N"
  }
}

TEST_DF$DEFINITION <- apply(TEST_DF[, c(2:5), with = FALSE], 1, 
                            FUN = IN_FUN(TEST_DF[, c(2:5), with = FALSE], TEST_DF$DAYS))

DESIRED <- TEST_DF <- data.table(P_ID = c(1,2,3,4),
                                 COL_1 = c(0,0,0,0),
                                 COL_2 = c(0,0,2,5),
                                 COL_3 = c(0,0,0,0),
                                 COL_4 = c(0,2,0,1),
                                 DAYS  = c(4,4,2,2).
                                 DEFINITION = c("Y","N","Y","N"),
                                 INDEX = c(2,NA,4,NA)

对于第一行,我想看看四个0是否在COL_1到COL_4之内,第二行中有四个0,第三行和第四行中有两个0。基本上,0的数量由DAYS列中的值给出。因此,由于四个0在行1内,因此DEFINITION得到值“Y”,第二行得到值“N”,因为只有三个0的行4应该得到值“Y”,因为有两个0,等等点。

此外,如果可能,如果DEFINITION列中的值为“Y”,则它应返回所需序列的第一次出现的列索引,例如,在第1行中,因为我们正在寻找的4 0中第一次出现0是在COL_1中然后我们应该为INDEX列获得值2,而第2行得到NA,因为DEFINITION是“N”,等等。 BR />

随意进行任何编辑,让其他用户更清楚,如果您需要更好的信息,请告知我们。

提前干杯:)

修改
下面是一个稍微扩展的数据表。如果这足够,请告诉我。

TEST_DF <- data.table(P_ID = c(1,2,3,4,5,6,7,8,10),
                  COL_1 = c(0,0,0,0,0,0,0,5,90),
                  COL_2 = c(0,0,0,0,0,0,3,78,6),
                  COL_3 = c(0,0,0,0,0,0,7,5,0),
                  COL_4 = c(0,0,0,0,0,5,0,2,0),
                  COL_5 = c(0,0,0,0,0,7,2,0,0),
                  COL_6 = c(0,0,0,0,0,9,0,0,5),
                  COL_7 = c(0,0,0,0,0,1,0,0,6),
                  COL_8 = c(0,0,0,0,0,0,0,1,8),
                  COL_9 = c(0,0,0,0,0,1,6,1,0),
                  COL_10 = c(0,0,0,0,0,0,7,1,0),
                  COL_11 = c(0,0,0,0,0,0,8,3,0),
                  COL_12 = c(0,0,0,0,0,0,9,6,7),
                  DAYS = c(10,8,12,4,5,4,3,4,7))

行的DEFINITION列为c(1,1,1,1,1,0,1,0,0),其中1为“Y”,0为“N”。要么没问题。

对于新编辑中的INDEX列,值应为c(2,2,2,2,2,NA,7,NA,NA)

3 个答案:

答案 0 :(得分:1)

我认为我现在已经对这个问题进行了一些编辑,我对此有了更好的理解。这有循环所以它可能不是最佳的速度,但set语句应该有助于此。它仍然具有data.table提供的一些加速功能。

#Combined all column values in giant string
TEST_DF[ , COL_STRING := paste(COL_1,COL_2,COL_3,COL_4,COL_5,COL_6,COL_7,COL_8,COL_9,COL_10,COL_11,COL_12,sep=",")]
TEST_DF[ , COL_STRING := paste0(COL_STRING,",")]

#Using the Days variable, create a string to be searched
for (i in 1:nrow(TEST_DF))
  set(TEST_DF,i=i,j="FIND",value=paste(rep("0,",TEST_DF[i]$DAYS),sep="",collapse=""))

#Find where pattern starts. A negative 1 value means it does not exist
for (i in 1:nrow(TEST_DF))
  set(TEST_DF,i=i,j="INDEX",value=regexpr(TEST_DF[i]$FIND,TEST_DF[i]$COL_STRING,fixed=TRUE)[1])

#Define DEFINITION
TEST_DF[ , DEFINITION := 1*(INDEX != -1)]

#Find where pattern starts. A negative 1 value means it does not exist
require(stringr)
for (i in 1:nrow(TEST_DF))
  set(TEST_DF,i=i,j="INDEX",value=str_count(substr(TEST_DF[i]$COL_STRING,1,TEST_DF[i]$INDEX),","))

#Clean up variables
TEST_DF[ , INDEX := INDEX + DEFINITION*2L]
TEST_DF[INDEX==0L, INDEX := NA_integer_]

答案 1 :(得分:1)

能够通过一些数学技巧来做到这一点。我创建了一个二进制矩阵,如果元素最初为0,则元素为1,否则为0。然后,对于每一行,我将行中的第n个元素设置为等于第(n-1个元素+第n个元素)乘以第n个元素。在此变换矩阵中,元素的值等于0的连续先前元素的数量(包括此元素)。

m<-as.matrix(TEST_DF[,2:(ncol(TEST_DF)-1),with=FALSE])
m[m==1]<-2
m[m==0]<-1
m[m!=1]<-0

for(i in 2:ncol(m)){
  m[,i]=(m[,i-1]+m[,i])*m[,i]
}

m<-as.matrix(cbind(m, Days=TEST_DF[,ncol(TEST_DF),with=FALSE]))
indx<-apply(m[,-ncol(m)] >= m[,ncol(m)],1,function(x) match(TRUE,x) )

TEST_DF$DEFINITION<-ifelse(is.na(indx),0,1)
TEST_DF$INDEX<-indx-TEST_DF$DAYS+2

注意:我偷了this post

中的一些东西

答案 2 :(得分:1)

您可以浏览IRanges包。我刚刚将测试数据集定义为data.frame,因为我不熟悉data.table。然后我将其扩展到您的数据集大小800000:

TEST_DF <- TEST_DF[sample(nrow(TEST_DF), 800000, replace=TRUE),]

然后,我们让IRanges工作:

library(IRanges)
m <- t(as.matrix(TEST_DF[,2:13]))
l <- relist(Rle(m), PartitioningByWidth(rep(nrow(m), ncol(m))))
r <- ranges(l)
validRuns <- width(r) >= TEST_DF$DAYS
TEST_DF$DEFINITION <- sum(validRuns) > 0
TEST_DF$INDEX <- drop(phead(start(r)[validRuns], 1)) + 1L

第一步是将表简化为矩阵,因此我们可以在适当的布局中转置和获取数据,以便将数据的轻量级分区(PartitioningByWidth)转换为一种列表。沿途将数据转换为行程编码(Rle),查找每行中的零运行。我们可以提取表示运行的ranges,然后比直接在分割Rle上更有效地计算它们。我们发现运行符合或超过DAYS并记录哪些组(行)至少有一次这样的运行。最后,我们找到有效运行的start,使用pheaddrop为每个组开始第一次启动,以便那些没有运行的运行成为NA

对于800,000行,这大约需要4秒。如果这还不够快,我们可以进行优化。