【问题标题】:Refer to columns by number in := assignment when using data.table使用 data.table 时在 := 赋值中按编号引用列
【发布时间】:2012-05-25 13:55:52
【问题描述】:

根据Row wise matrix operations in R,我想在我拥有的 data.table 上应用逐行函数。我希望每行计算该行中许多列的平均值。我目前的尝试是:

columns <- c(1,5,10,15,20) # Actually obtained via grep
my.data.table[,"average" := mean(columns),with=FALSE] # Or...
my.data.table[,average := mean(columns)]

不幸的是,这只是返回“列”向量的平均值,而不是它们所引用的列的平均值。有没有办法按数字引用这些列?

这是我想要达到的平均水平:

key  a b c average
A    5 5 5 5
B    1 2 3 2
C    2 4 9 5

【问题讨论】:

  • 没问题,感谢您的努力!让我们看看其他人是否可以找到解决方案。

标签: r data.table


【解决方案1】:

另一种选择是构建您真正想要执行的调用,然后在DT[] 中构建eval()。这是 data.table FAQ 的第 1.5 和 1.6 节中描述的策略(通过键入vignette("datatable-faq") 查看)。

这种方法的运行速度比涉及 rowMeans() 的方法快 3-5 倍。 (这种差异是由于rowMeans()'最初将 data.frames 转换为矩阵的耗时,正如 Matthew Dowle 在下面的 cmets 中指出的那样。)

## Prepare data
library(data.table)
N <- 1000000
DT <- data.table(ID = 1:N,
                 Year1 = rnorm(N),
                 Year2 = rnorm(N),
                 Year3 = rnorm(N),
                 Year4 = rnorm(N))    
x <- c(2, 3, 4, 5)

## Construct the desired expression:   (Year1 + Year2 + Year3 + Year4)/4
addCols <- paste(names(DT)[x], collapse = " + ")
e <- paste("(", addCols, ")/", length(x), sep="")
e <- parse(text=e)[[1]]

## Compare timings
system.time(x2 <- DT[,eval(e)])
#    user  system elapsed 
#    0.11    0.00    0.11 
system.time(x1 <- rowMeans(DT[, ..x]))
#    user  system elapsed 
#    0.53    0.14    0.77 

## Check results
# all.equal(x1,x2)
# [1] TRUE

【讨论】:

  • +10 不错!见rowMeans 的第一行:if (is.data.frame(x)) x=as.matrix(x)。所以这是首先复制到matrix 结构中。这与rowMeansuserelapsed 之间存在差异相吻合,而直接使用eval 可以避免这种差异。将N 乘以10,然后再乘以10,差异应该会扩大。
  • @MatthewDowle - 是的,就是这样。感谢您跟踪!
  • NP。您能否将my answer 回顾到投票数最高的data.frame 问题,如果可以的话,可以从0 开始吗​​?
【解决方案2】:

这里有两种可能的解决方案。它们基本上都来自您已经提供的链接,所以也许我错过了这个问题的一些内容。我们开始:

解决方案 1(使用 rowMeans):

library(data.table)
N <- 1000000
my.data.table <- data.table(ID = 1:N,
                            Year1 = rnorm(N),
                            Year2 = rnorm(N),
                            Year3 = rnorm(N),
                            Year4 = rnorm(N))

x <- c(2, 3, 4, 5)
system.time(x1 <- rowMeans(my.data.table[, ..x]))
   user  system elapsed 
   0.08    0.00    0.08

解决方案 2:首先将其转换为长格式。我认为这更快,主要是因为 Matthew 在另一个问题中的评论说 data.table 是针对 DT[,mad(variable),by=group] 语法的。我想我错过了什么,但看不到什么:

library(reshape2)
DT <- as.data.table(melt(as.data.frame(my.data.table), id.var="ID"))
setkey(DT, ID)
system.time(x2 <- DT[, mean(value), by="ID"][[2]])
   user  system elapsed 
  11.28    0.00   11.33 
all.equal(x1, x2)
[1] TRUE

【讨论】:

  • +1 我无法击败 0.08。在这种情况下,没有任何分组。在每行都是一个组的情况下进行分组并不是真正的分组。我同意rowMeansother question 中是cmets 中最好的(afaik),并且还提到了“准系统”.colSums().rowSums().colMeans().rowMeans(),其中需要最高速度,添加到R 2.15.0。
  • 好的,很高兴知道我在这里没有遗漏任何东西。感谢您的澄清。
  • @MatthewDowle 和 Christoph_J -- 看起来我找到了快 3-5 倍的东西。如果你们中的任何一个了解为什么它会这么快,就会很感兴趣。
【解决方案3】:

好吧,再来一次……

这样可以吗

x<-1:5
y<-1:5
z<-1:5
xy<-data.table(x,y,z)
id<-c("x","y")
newxy<-rowMeans(xy[, id, with=FALSE])

【讨论】:

  • 该语法不适用于 data.table 并且 colMeans 在这里并不适用。
  • 编辑的唯一问题是 cmets 现在不匹配。因此,为了澄清,rowMeansdata.table 确实可以正常工作,Ina 的评论是关于原来的答案,它做了其他事情。
猜你喜欢
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 1970-01-01
  • 2021-07-23
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多