【问题标题】:Selecting rows or columns with data.table R?使用 data.table R 选择行或列?
【发布时间】:2015-04-23 00:48:54
【问题描述】:

假设我有一个 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

如何从中获取 data.table,其中所有 ROWS 的任何列都包含“0”? (或一些价值)
如果我必须用一个列来做,我可以使用:

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 语法。

同样,您如何获得一个 data.table,其中所有列的任何行都包含“0”?

我知道如何获取满足条件的列(作为一个整体),例如数字,

RRR[,sapply(RRR,function(xx)is.numeric(xx)),with=FALSE]

但如果我想逐元素测试条件,则此方法不起作用。


如果有人感兴趣,这是一个更大的随机 data.table 的 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)] &lt;- NA; na.omit(RRR)

【问题讨论】:

  • 更新时间
  • 很高兴看到时间安排,我已经添加了另一个,不确定但也许 as.logical 会比 == 快。
  • 是的,你是对的:出于某种原因,我的Reduce 方法确实会产生不同的结果。顺便说一句,检查这一点的一种方法(对于“获取 ROWS”操作)是只比较选定的行:myrows1 &lt;- RRR[RRR[,rowSums(RRR==0)&gt;0],.I]myrows2 &lt;- RRR[rowSums(RRR==0)&gt;0,.I] 等等。您可以使用identicalsetdiff 函数对它们进行比较。 (在比较时间之后使用identical 进行验证似乎是相当标准的。)
  • 关于你的最后一段,我认为(@BrodieG)说过apply 的行选择比rowSums 慢(不是sapply 与列选择相关)。那里的测试将是system.time(RRR[apply(RRR,1,function(x)any(!x))])。我发现这需要 system.time(RRR[rowSums(!RRR)&gt;0] ) 的 6 倍,所以他是对的。
  • 弗兰克,我必须修正你的 Reduce 方法。 RRR[Reduce(|,lapply(RRR,!))] 或者通用方式:RRR[Reduce('|',lapply(RRR,function(xx)(xx==0)))]跨度>

标签: r select row data.table


【解决方案1】:

rowSums 函数可以在这里使用:

RRR[rowSums(!RRR)>0]

工作原理: !RRR 是一个矩阵,TRUE 为任意零。在一般情况下,您可以将!RRR 替换为您想要检查的任何逻辑条件。例如,要查看是否有任何元素等于 3,您可以取 rowSumsRRR==3

我认为rowSums(test(x))&gt;0apply(RRR,1,function(x)any(!test(x)))本质上是一样的;两者都将对象强制为矩阵。我发现rowSums 版本更易于阅读,并且我认为我听到人们称赞它的效率。


对于列,类似地:

RRR[, colSums(!RRR)>0, with=FALSE]

【讨论】:

  • 您的解决方案有效,但前提是您尝试匹配的值为零。一般来说,你会怎么做? (对于 7、NA 或“食物”等值)
  • [.listof 是什么?
  • [.listof 命令是我通过键入methods(`[`,data.table) 发现的一个不起眼的命令。它适用于任何类型的列表,包括 data.tables 和 data.frames。我觉得它应该列在class(RRR) 上,但事实并非如此。可以通过is.list(RRR)进行验证。
  • rowSums 比涉及apply 的任何事情都要快。矩阵会有强制转换,但对我来说,整个 OP 工作流程不能在矩阵中完成并不明显,在这种情况下不会有转换。此外,我认为 Arun 提到他正在为 data.table 开发类似 rowSums 的东西。
  • @Frank 感谢[.listof,我一定会用的!
【解决方案2】:

也许是这样。

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 CRAN 版本不能被视为最新版本,每个强大开发的包都是这种情况。转到githubtravis,您将拥有两个世界中最好的。如果您不相信,请阅读1.9.5 news/readme
  • 好的,由于不兼容问题,这并不容易,但现在我安装了 data.table 1.9.5。时间几乎相同,现在您的代码可以工作了。
  • Jan Gorecki 我已经用你最后的代码更新了第一篇文章,它更快,虽然只对测试零有用。您发现使用“.SDcols”有什么优势?
  • 使代码语句更加动态,.SDcols 简单地将提供的列定义为字符、整数或逻辑。只有选定的列将包含在.SD data.table 对象中进行操作。
猜你喜欢
  • 1970-01-01
  • 2023-01-27
  • 2023-03-12
  • 1970-01-01
  • 2019-01-15
  • 2015-05-16
  • 2015-03-21
  • 2011-08-16
相关资源
最近更新 更多