【问题标题】:Idiom for dropping a single column in a data.table在 data.table 中删除单个列的成语
【发布时间】:2013-05-10 00:26:30
【问题描述】:

我需要从包含几百列的 data.frame 中删除一列。

使用data.frame,我会使用subset 方便地执行此操作:

> dat <- data.table( data.frame(x=runif(10),y=rep(letters[1:5],2),z=runif(10)),key='y' )
> subset(dat,select=c(-z))
            x y
 1: 0.1969049 a
 2: 0.7916696 a
 3: 0.9095970 b
 4: 0.3529506 b
 5: 0.4923602 c
 6: 0.5993034 c
 7: 0.1559861 d
 8: 0.9929333 d
 9: 0.3980169 e
10: 0.1921226 e

显然这仍然有效,但它似乎不是一个非常类似于data.table 的成语。我可以手动构建一个我想保留的列名列表,这看起来更像data.table-like:

> dat[,list(x,y)]
            x y
 1: 0.1969049 a
 2: 0.7916696 a
 3: 0.9095970 b
 4: 0.3529506 b
 5: 0.4923602 c
 6: 0.5993034 c
 7: 0.1559861 d
 8: 0.9929333 d
 9: 0.3980169 e
10: 0.1921226 e

但是我必须构建这样一个列表,这很笨重。

subset 是方便地删除一两列的正确方法,还是会导致性能下降?如果没有,有什么更好的方法?

编辑

基准测试:

> dat <- data.table( data.frame(x=runif(10^7),y=rep(letters[1:10],10^6),z=runif(10^7)),key='y' )
> microbenchmark( subset(dat,select=c(-z)), dat[,list(x,y)] )
Unit: milliseconds
                         expr       min        lq    median        uq      max
1           dat[, list(x, y)] 102.62826 167.86793 170.72847 199.89789 792.0207
2 subset(dat, select = c(-z))  33.26356  52.55311  53.53934  55.00347 180.8740

但如果subset 复制整个data.table,那么内存可能更重要。

【问题讨论】:

  • 一个问题:你打算删除这个专栏是什么?我的意思是你打算如何处理生成的 data.table?
  • @Arun 让我问的特殊情况是,我在使用:= 创建时错误地命名了一个列并想删除它。但总是会出现转储一两列很方便的情况。
  • 正如@mnel 解释的那样,如果您只想删除,您可以直接使用:= 运算符,如果您想在没有此列的情况下执行计算,那么您始终可以使用.SDcols。因此,您可以完全避免 subset / with=FALSE 解决方案。

标签: r data.table


【解决方案1】:

如果您想永久删除该列,请使用:= NULL

dat[, z := NULL]

如果您要将列作为字符串删除,请使用 () 强制评估为字符串,而不是字符名称。

toDrop <- c('z')

dat[, (toDrop) := NULL]

如果你想限制.SD中列的可用性,你可以传递.SDcols参数

dat[,lapply(.SD, somefunction) , .SDcols = setdiff(names(dat),'z')]

但是,data.table 会检查 j 参数并仅获取您以任何方式使用的列。请参阅常见问题解答 1.12

当你写 X[Y,sum(foo*bar)], data.table 自动检查 j 表达式以查看它使用了哪些列。

并且不会尝试加载.SD 的所有数据(除非您在对j 的调用中有.SD


subset.data.table 正在处理调用并最终评估 dat[, c('x','y'), with=FALSE]

使用:= NULL 应该基本上是即时的,但是确实会永久删除该列。

【讨论】:

  • mnel 说:“使用()强制评估作为一个字符串”....我一直试图弄清楚如何做到这一点好几个星期。这个小技巧值得自己进行问答。
【解决方案2】:

我想这就是你要找的。​​p>

dat[, !"z"]

这是您编辑的大量数据的基准。

Unit: milliseconds
                         expr       min        lq    median       uq      max neval
  subset(dat, select = c(-z))  53.37435  56.82514  61.81279 100.3458 339.1400   100
            dat[, list(x, y)] 191.46678 354.39905 412.06421 451.3933 678.3981   100
                  dat[, !"z"]  53.49184  57.31756  62.15506 112.7063 398.0107   100

【讨论】:

  • subset.data.table 与您的通话相同,因为他们正在做同样的事情,请参阅getAnywhere('subset.data.table')
  • 我更喜欢这种语法而不是subset,因为this post。我假设同样的论点可以扩展到data.table
  • @Arun 关心解释这是如何工作的?例如,为什么with 选项必须是FALSE
  • with=TRUE(默认)时,j 在您的 data.table 范围内进行评估。也就是说,它将 data.table 的列“视为”变量。为了克服这个默认值并获得与 data.frame 中相同的行为,我们设置了with=FALSE。如果不是,DT[, "x"] 将返回"x",因为"x" 的计算结果为"x"Faq 1.1 and 1.2 可能值得一读以了解更多信息。
猜你喜欢
  • 2022-01-13
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-01
  • 2015-07-07
  • 2014-08-26
相关资源
最近更新 更多