新的data.table columnS基于多列的分组和功能

时间:2017-04-09 01:41:02

标签: r data.table

假设我有data.frame

sample_df = structure(list(AE = c(148, 1789, 1223, 260, 1825, 37, 1442, 484, 
10, 163, 1834, 254, 445, 837, 721, 1904, 1261, 382, 139, 213), 
    FW = structure(c(1L, 3L, 2L, 3L, 3L, 1L, 2L, 3L, 2L, 2L, 
    3L, 2L, 3L, 2L, 1L, 3L, 1L, 1L, 1L, 3L), .Label = c("LYLR", 
    "OCXG", "BIYX"), class = "factor"), CP = c("WYB/NXO", "HUK/NXO", 
    "HUK/WYB", "HUK/NXO", "WYB/NXO", "HUK/WYB", "HUK/NXO", "HUK/NXO", 
    "WYB/NXO", "HUK/NXO", "WYB/NXO", "HUK/NXO", "HUK/WYB", "WYB/NXO", 
    "HUK/WYB", "WYB/NXO", "WYB/NXO", "HUK/WYB", "WYB/NXO", "WYB/NXO"
    ), SD = c(1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 
    -1, 1, -1, 1, 1, 1)), .Names = c("AE", "FW", "CP", "SD"), row.names = c(NA, -20L), class = "data.frame")

或以人类可读的格式:

     AE   FW      CP SD
1   148 LYLR WYB/NXO  1
2  1789 BIYX HUK/NXO  1
3  1223 OCXG HUK/WYB -1
4   260 BIYX HUK/NXO  1
5  1825 BIYX WYB/NXO  1
6    37 LYLR HUK/WYB  1
7  1442 OCXG HUK/NXO  1
8   484 BIYX HUK/NXO -1
9    10 OCXG WYB/NXO  1
10  163 OCXG HUK/NXO  1
11 1834 BIYX WYB/NXO -1
12  254 OCXG HUK/NXO -1
13  445 BIYX HUK/WYB  1
14  837 OCXG WYB/NXO -1
15  721 LYLR HUK/WYB -1
16 1904 BIYX WYB/NXO  1
17 1261 LYLR WYB/NXO -1
18  382 LYLR HUK/WYB  1
19  139 LYLR WYB/NXO  1
20  213 BIYX WYB/NXO  1

现在假设对于(fw,cp)的每个唯一值(FW,CP),我想得到

  • AE
  • (FW,CP)=(fw,cp)的所有值的总和
  • SD
  • (FW,CP)=(fw,cp)的所有值的平均值

在R中,人们可以做类似的事情:

unique_keys  <- unique(sample_df[,c('FW','CP')])
slow_version <- function(ind, sample_df, unique_keys){
    index <- which(sample_df$FW == unique_keys$FW[ind] & sample_df$CP == unique_keys$CP[ind])
    c(ind    = ind,
               sum_ae = sum(sample_df$AE[index]), 
               min_ae = mean(sample_df$SD[index]))
}
intermed_result <- t(sapply(1:nrow(unique_keys), slow_version, 
                                      sample_df = sample_df, 
                                    unique_keys = unique_keys))
colnames(intermed_result) <- c('ind','sum','mean')
result <- data.frame(unique_keys[intermed_result[, 'ind'], ], 
                    'sum' = intermed_result[,'sum'], 
                    'mean' = intermed_result[,'mean'])

但随着data_frame的大小增加,这变得非常缓慢。

感谢this回答,我怀疑可以使用data.table魔法快速获得相同的结果。但是这样做:

library(data.table)
sample_dt = data.table(sample_df)
setkey(sample_dt, FW, CP)
f <- function(AE, SD) {list('sum' = sum(AE), 'mean' = mean(SD))} 
sample_dt[,c("col1","col2"):=f(AE, SD), by=.(FW, CP)][]

不会产生预期的结果。什么是正确的方法?

1 个答案:

答案 0 :(得分:2)

我会尝试:

library(data.table)
sample_dt = data.table(data_frame)
setkey(sample_dt, FW, CP)
f <- function(AE, SD) {list('sum' = sum(AE), 'mean' = mean(SD))} 

sample_dt[, f(AE, SD), by=.(FW, CP)]
#      FW      CP  sum       mean
# 1: LYLR HUK/WYB 1140  0.3333333
# 2: LYLR WYB/NXO 1548  0.3333333
# 3: OCXG HUK/NXO 1859  0.3333333
# 4: OCXG HUK/WYB 1223 -1.0000000
# 5: OCXG WYB/NXO  847  0.0000000
# 6: BIYX HUK/NXO 2533  0.3333333
# 7: BIYX HUK/WYB  445  1.0000000
# 8: BIYX WYB/NXO 5776  0.5000000

您没有获得所需的输出,因为您将结果总和和平均值按列分配到原始data.table :=。但是,我也更喜欢Frank建议的语法,这应该是正确的方法。对于我们当前的命名列表方法,在添加verbose = T时,它会显示:

  

制作每个组并运行j(GForce FALSE)... j的结果是   命名列表。在和上创建相同的名称效率非常低   每个小组再一次。当j = list(...)时,检测到任何名称,   在分组完成后移除并放回,以提高效率。   例如,使用j = transform()可以防止加速(考虑   改为:=)。 此邮件可能会在将来升级为警告

当我们有多个群组且j中的功能是meansd等基本功能时,使用

sample_dt2[, .(sum.AE = sum(AE), mean.SD = mean(SD)), by=.(FW, CP)]

会非常快,因为这些函数在内部被gmean替换为GForce函数。有关详细信息,请参阅?GForcethe benchmark of Frank