我想计算具有大型数据集的多个组合的NA的平均值和计数。对于某些测试数据,这可能是最容易解释的。我在Macbook Pro上使用最新版本的R和data.table包(数据很大,> 1M行)。 (注意:在发布之后我注意到我不小心使用了sum()而不是mean()用于下面的“m =”变量。我没有编辑它,因为我不想重新运行所有内容,并且不要认为这很重要)
set.seed(4)
YR = data.table(yr=1962:2015)
ID = data.table(id=10001:11000)
ID2 = data.table(id2 = 20001:20050)
DT <- YR[,as.list(ID), by = yr] # intentional cartesian join
DT <- DT[,as.list(ID2), by = .(yr, id)] # intentional cartesian join
rm("YR","ID","ID2")
# 2.7M obs, now add data
DT[,`:=` (ratio = rep(sample(10),each=27000)+rnorm(nrow(DT)))]
DT <- DT[round(ratio %% 5) == 0, ratio:=NA] # make some of the ratios NA
DT[,`:=` (keep = as.integer(rnorm(nrow(DT)) > 0.7)) ] # add in the indicator variable
# do it again
DT[,`:=` (ratio2 = rep(sample(10),each=27000)+rnorm(nrow(DT)))]
DT <- DT[round(ratio2 %% 4) == 0, ratio2:=NA] # make some of the ratios NA
DT[,`:=` (keep2 = as.integer(rnorm(nrow(DT)) > 0.7)) ] # add in the indicator variable
所以,我所拥有的是识别信息(yr,id,id2)和我要总结的数据:keep1 | 2,ratio1 | 2。特别是通过yr-id,我想使用keep和keep2计算平均比率和ratio2(从而压缩id2)。我想通过保持/保持2计算比率和比率2或通过保持*比率,保持2 *比率,保持*比率2和保持2 *比率2的矩阵乘法来实现这一点。
首先,我这样做的方式得到了正确的答案,但速度很慢:
system.time(test1 <- DT[,.SD[keep == 1,.(m = sum(ratio,na.rm = TRUE),
nmiss = sum(is.na(ratio)) )
],by=.(yr,id)])
user system elapsed
23.083 0.191 23.319
这几乎同时适用。我认为首先对主数据进行子集而不是在.SD中进行子集可能更快:
system.time(test2 <- DT[keep == 1,.SD[,.(m = sum(ratio,na.rm = TRUE),
nmiss = sum(is.na(ratio)) )
],by=.(yr,id)])
user system elapsed
23.723 0.208 23.963
这些方法中的任何一个的问题是我需要为每个keep
变量进行单独的计算。因此我尝试了这种方式:
system.time(test3 <- DT[,.SD[,.( m = sum(ratio*keep,na.rm = TRUE),
nmiss = sum(is.na(ratio*keep)) )
],by=.(yr,id)])
user system elapsed
25.997 0.191 26.217
这允许我将所有公式放在一起(我可以添加ratio*keep2
,ratio2*keep
和ratio2*keep2
)但是1.它更慢而且2.它没有得到正确的NA的数量(参见nmiss
列):
> summary(test1)
yr id m nmiss
Min. :1962 Min. :10001 Min. : -2.154 Min. :0.000
1st Qu.:1975 1st Qu.:10251 1st Qu.: 30.925 1st Qu.:0.000
Median :1988 Median :10500 Median : 53.828 Median :1.000
Mean :1988 Mean :10500 Mean : 59.653 Mean :1.207
3rd Qu.:2002 3rd Qu.:10750 3rd Qu.: 85.550 3rd Qu.:2.000
Max. :2015 Max. :11000 Max. :211.552 Max. :9.000
> summary(test2)
yr id m nmiss
Min. :1962 Min. :10001 Min. : -2.154 Min. :0.000
1st Qu.:1975 1st Qu.:10251 1st Qu.: 30.925 1st Qu.:0.000
Median :1988 Median :10500 Median : 53.828 Median :1.000
Mean :1988 Mean :10500 Mean : 59.653 Mean :1.207
3rd Qu.:2002 3rd Qu.:10750 3rd Qu.: 85.550 3rd Qu.:2.000
Max. :2015 Max. :11000 Max. :211.552 Max. :9.000
> summary(test3)
yr id m nmiss
Min. :1962 Min. :10001 Min. : -2.154 Min. : 0.00
1st Qu.:1975 1st Qu.:10251 1st Qu.: 30.925 1st Qu.: 2.00
Median :1988 Median :10500 Median : 53.828 Median : 4.00
Mean :1988 Mean :10500 Mean : 59.653 Mean : 4.99
3rd Qu.:2002 3rd Qu.:10750 3rd Qu.: 85.550 3rd Qu.: 8.00
Max. :2015 Max. :11000 Max. :211.552 Max. :20.00
通过yr-id获取我的4种摘要信息组合的最快方法是什么? 现在,我使用选项1或2重复两次(一次用于保持,再次用于keep2)
答案 0 :(得分:1)
您可以直接在j
:
# solution A: summarize in `.SD`:
system.time({
test2 <- DT[keep == 1,
.SD[, .(m = sum(ratio, na.rm = TRUE),
nmiss = sum(is.na(ratio)))],
by = .(yr, id), verbose = T]
})
# user system elapsed
# 22.359 0.439 22.561
# solution B: summarize directly in j:
system.time({
test2 <- DT[keep == 1, .(m = sum(ratio, na.rm = T),
nmiss = sum(is.na(ratio))),
by = .(yr, id), verbose = T]
})
# user system elapsed
# 0.118 0.077 0.195
添加了 verbose = T
以显示两种方法之间的区别:
解决方案A:
lapply优化已开启,j未更改为&#39; .SD [,list(m = sum(ratio, na.rm = TRUE),nmiss = sum(is.na(ratio)))]&#39; GForce开启了,左边是j 不变
旧平均优化已开启,左侧j未更改。
制作每个小组并运行j(GForce FALSE)...... j的结果是
命名列表。在和上创建相同的名称效率非常低 每个小组都重复一次。
当j = list(...)时,检测到任何名称, 在分组完成后移除并放回,以提高效率。 例如,使用j = transform()可以防止加速(考虑 改为:=)。此消息可能会在将来升级为警告。
收集不连续的群体为54000组收取0.058s 对于54000个电话,eval(j)花费了22.487秒 22.521秒
对于解决方案B:
...
从位置查找组大小(可以避免保存RAM) ... 0秒lapply优化打开,j未更改为&#39; list(sum(ratio, na.rm = T),sum(is.na(ratio)))&#39;
GForce开启,左边j不变
旧平均优化打开,左边j不变。制作每个小组和 运行j(GForce FALSE)......收集不连续的群体 对于54000组,0.027s eval(j)为54000次呼叫花了0.079s 0.168秒
主要区别在于B中的摘要被视为命名列表,当有许多组时(这个数据为54k组!),这是非常慢的。对于此类型的类似基准,请参阅this one。
对于第二部分(你的test3):
我们首先没有按keep = 1
过滤列。因此NA
中的keep !=
nmiss
也计入NA
。因此,{{1}}的计数不同。