使用data.table R选择行或列?

5

假设我有一个data.table,例如:

library(data.table) 
RRR <-data.table(1:15,runif(15),rgeom(15,0.5),rbinom(15,2,0.5))

    V1      V2    V3  V4
 1:  1 0.33577273  0  0
 2:  2 0.66739739  2  1
 3:  3 0.07501655  0  0
 4:  4 0.43195663  2  1
 5:  5 0.39525841  3  2
 6:  6 0.15189738  1  1
 7:  7 0.02637279  0  1
 8:  8 0.44165623  0  1
 9:  9 0.98710570  2  0
10: 10 0.62402805  1  0
11: 11 0.84829465  3  2
12: 12 0.02170976  0  1
13: 13 0.74608925  0  2
14: 14 0.29102296  2  0
15: 15 0.83820646  1  1

我该如何从中获取一个包含所有包含“0”(或某个值)的行的data.table呢?
如果只有一列,我可以使用以下代码:

RRR[V4==0,]

   V1    V2      V3  V4
1:  1 0.33577273  0  0
2:  3 0.07501655  0  0
3:  9 0.98710570  2  0
4: 10 0.62402805  1  0
5: 14 0.29102296  2  0

但如果我想一次性处理所有列,因为我有很多列怎么办?

这种方法并不能满足我的需求。

RRR[,sapply(RRR,function(xx)(xx==0)), with=TRUE]   

     V1      V2     V3    V4
[1,]  FALSE FALSE  TRUE  TRUE
[2,]  FALSE FALSE FALSE FALSE
[3,]  FALSE FALSE  TRUE  TRUE
[4,]  FALSE FALSE FALSE FALSE
[5,]  FALSE FALSE FALSE FALSE
[6,]  FALSE FALSE FALSE FALSE
[7,]  FALSE FALSE  TRUE FALSE
[8,]  FALSE FALSE  TRUE FALSE
[9,]  FALSE FALSE FALSE  TRUE
[10,] FALSE FALSE FALSE  TRUE
[11,] FALSE FALSE FALSE FALSE
[12,] FALSE FALSE  TRUE FALSE
[13,] FALSE FALSE  TRUE FALSE
[14,] FALSE FALSE FALSE  TRUE
[15,] FALSE FALSE FALSE FALSE

也许可以用for循环和一些复杂的粘贴操作来实现?不过,我更喜欢使用简单的data.table语法。
同样地,如何获取包含任何行中的“0”的所有列的data.table?
我知道如何获取满足条件的列(整个列),比如是数值类型的。
RRR[,sapply(RRR,function(xx)is.numeric(xx)),with=FALSE]

但是如果我想逐个测试条件元素,则该方法无法工作。


如果有人感兴趣,这是使用您目前提供的不同解决方案对较大随机数据表进行系统时间(system.time())测试的结果,稍作修改。

set.seed(1)
n <- 1000000
RRR <- data.table(matrix(rgeom(100*n,0.5), ncol=100))

Getting ROWS   
> RRR[RRR[,rowSums(RRR==0)>0]] 
   user  system elapsed 
   2.72    0.55    3.27 
> RRR[rowSums(RRR==0)>0] 
   user  system elapsed 
   2.58    0.70    3.28 
> RRR[apply(RRR,MAR=1,function(xx)any(xx==0))]
   user  system elapsed 
   10.81    0.19   11.00       
> RRR[apply(RRR[,paste0('V',1:ncol(RRR)),with=FALSE],function(xx)any(xx==0),MAR=1)]
  user  system elapsed 
  10.49    0.30   10.83 

Getting COLUMNS
> RRR[,sapply(RRR,function(xx)any(xx==0)), with=FALSE] 
   user  system elapsed 
   0.81    0.31    1.12 
> `[.listof`(RRR,colSums(RRR==0)>0) 
   user  system elapsed 
   2.14    0.27    2.41 
> RRR[,colSums(RRR==0)>0, with=FALSE] 
   user  system elapsed 
   2.26    0.48    2.75 
> RRR[, .SD, .SDcols=sapply(RRR, function(x) any(x==0))]      #only version 1.9.5, seems the same solution than the first one.
   user  system elapsed 
   0.78    0.36    1.14 
> RRR[, .SD, .SDcols=sapply(RRR, function(x) any(!as.logical(x)))]
   user  system elapsed 
   0.41    0.25    0.66 
> RRR[Reduce('|',lapply(RRR,function(xx)(xx==0)))]
   user  system elapsed 
   3.11    0.33    3.44 
> RRR[,apply(RRR[,paste0('V',1:ncol(RRR)),with=FALSE],function(xx)any(xx==0),MAR=2),with=FALSE]
   user  system elapsed 
   3.48    0.80    4.28  

我还没有包含:

RRR[, i := any(unlist(lapply(.SD, function(x) x==0))), seq_len(nrow(RRR))][i==TRUE][,i:=NULL]   

几分钟后我停止了它,并且它“标记”行而不是提取它们,这是最复杂的解决方案。 我会等待更快或更简单的解决方案,并听取您的意见和喜好。
sapply应该会更慢,但实际上它并没有变慢。如果data.table包含其他类型的数据,则结果可能会改变。
如果我们能在每行或每列中的第一次出现时停止测试(==0),我们可以加速它。但我想我们不能做到没有循环或某些低级访问或位运算。
我想出了一种新方法。
1. sapply(RRR,function(xx)which(xx==0)) 2. 我需要将a)的结果与列表的联合组合起来,但我不知道如何为任意数量的列执行此操作。 3. 然后获取那些行 RRR["a)"]
如果零的数量很大,我猜它会变得非常慢。
也许尝试RRR[unique(unlist(sapply(RRR,function(xx)which(xx==0))))],但它太慢了。
获取相反的选项是RRR[(RRR==0)] <- NA; na.omit(RRR)

已更新时间。 - skan
很高兴看到计时,我又加了一个,不确定但也许 as.logical== 更快。 - jangorecki
1
是的,你说得对:由于某种原因,我的“Reduce”方法确实会产生不同的结果。顺便说一下,检查这个(对于“获取行”操作)的一种方法是只比较所选行:myrows1 <- RRR[RRR[,rowSums(RRR==0)>0],.I]myrows2 <- RRR[rowSums(RRR==0)>0,.I]等等。您可以使用“identical”和“setdiff”函数进行比较。(在比较时间后使用“identical”似乎是相当标准的做法。) - Frank
1
关于您最后一段的内容,我认为(由@BrodieG提出)apply在行选择方面比rowSums慢(与列选择无关的sapply)。测试方法是system.time(RRR[apply(RRR,1,function(x)any(!x))])。我发现这需要6倍的时间才能完成system.time(RRR[rowSums(!RRR)>0] ),所以他是正确的。 - Frank
1
Frank,我得修复你的Reduce方法。 RRR[Reduce(`|',lapply(RRR,'!'))] 或者一般方式:RRR [Reduce('|',lapply(RRR,function(xx)(xx==0)))] - skan
显示剩余6条评论
2个回答

7

这里可以使用rowSums函数:

RRR[rowSums(!RRR)>0]
工作原理:!RRR是一个矩阵,其中任何零都为TRUE。在一般情况下,您可以将!RRR替换为您想要检查的任何逻辑条件。例如,要查看是否有任何元素等于3,您可以获取RRR==3rowSums
我认为rowSums(test(x))>0本质上与apply(RRR,1,function(x)any(!test(x)))相同;两者都将对象强制转换为矩阵。我发现rowSums版本更易于阅读,并且我认为人们赞扬它的效率。
对于列,类似地:
RRR[, colSums(!RRR)>0, with=FALSE]

你的解决方案有效,但仅适用于您尝试匹配的值为零的情况。对于一般情况下的值(例如7、NA或“food”),您该如何处理? - skan
什么是 [.listof - skan
1
[.listof命令是一个我通过输入methods([,data.table)发现的不太常用的命令。它适用于任何类型的列表,包括数据表和数据框。我觉得它应该在class(RRR)中列出,但实际上没有。可以使用is.list(RRR)进行验证。 - Frank
除了检查零之外,我还举了另一个例子。 - Frank
1
rowSums比任何涉及apply的东西都要快得多。会有一个矩阵强制转换,但我不确定整个OP工作流程是否不能在矩阵中完成,在这种情况下就不需要转换。此外,我认为Arun提到他正在为data.table开发类似于rowSums的功能。 - BrodieG
1
@Frank,感谢你提供的[.listof],我一定会用到它! - jangorecki

3
也许是这样。
library(data.table) 
RRR <-data.table(1:15,runif(15),rgeom(15,0.5),rbinom(15,2,0.5))
RRR[, i := any(unlist(lapply(.SD, function(x) x==0))), seq_len(nrow(RRR))
    ][i==TRUE
      ][,i:=NULL]

为第二部分问题提供更多解答。

 RRR[, .SD, .SDcols=sapply(RRR, function(x) any(x==0))]
 # you may add this one also to timing, I wonder how it will work
 RRR[, .SD, .SDcols=sapply(RRR, function(x) any(!as.logical(x)))]

.SDcols 作为逻辑向量是最近引入的,所以请确保首先更新您的 data.table。


Jan,我已经尝试过了,但是我收到了错误提示:“.SDcols应该是列号或列名”。我的data.table版本是1.9.4(在Windows上)。我正在计时不同的选项。 - skan
@skan CRAN版本不能被视为最新版本,这对于每个稳健开发的软件包都是如此。前往githubtravis,你将拥有两个世界的最佳体验。如果您还不确定,请阅读1.9.5新闻/自述文件 - jangorecki
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - skan
Jan Gorecki,我已经更新了第一篇帖子,使用了您的最新代码,它更快,但仅适用于测试零。您认为使用“.SDcols”有哪些优势? - skan
1
使代码语句更具动态性,.SDcols 只需定义以字符、整数或逻辑形式提供的列。只有选定的列将包含在 .SD 数据表对象中以进行操作。 - jangorecki

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