将表达式传递给data.table中的嵌套分组

时间:2012-06-29 16:09:17

标签: r data.table

我有一个类似于这个的data.table对象

library(data.table)

c <- data.table(CO = c(10000,10000,10000,20000,20000,20000,20000),
                SH = c(1427,1333,1333,1000,1000,300,350),
                PRC = c(6.5,6.125,6.2,0.75,0.5,3,3.5),
                DAT = c(0.5,-0.5,0,-0.1,NA_real_,0.2,0.5),
                MM = c("A","A","A","A","A","B","B"))

我试图使用嵌套分组执行计算,将表达式作为参数传递。这是我所拥有的简化版本:

setkey(c,MM)

mycalc <- quote({nobscc <- length(DAT[complete.cases(DAT)]); 
                 list(MKTCAP = tail(SH,n=1)*tail(PRC,n=1),
                      SQSUM = ifelse(nobscc>=2, sum(DAT^2,na.rm=TRUE), NA_real_),
                      COVCOMP = ifelse(nobscc >= 2, head(DAT,n=1), NA_real_),
                      NOBS = nobscc)}) 


myresults <- c[,.SD[,{setkey=CO; eval(mycalc)},by=CO],by=MM]

产生

     MM    CO MKTCAP SQSUM COVCOMP NOBS
[1,]  A 10000 8264.6  0.50     0.5    3
[2,]  A 20000  500.0    NA      NA    1
[3,]  B 20000 1225.0  0.29     0.2    2

在上面的示例中,我有两个使用ifelse构造的列表元素(在实际代码中有3个),所有进行相同的测试:如果数量为观察值大于2,然后执行某个计算(对于列表的每个元素是不同的,并且每个都可以写成函数),否则我希望这些元素的值为NA。这些元素的另一个共同点是它们使用我data.table的同一列:DAT

所以我的问题是:我有什么方法可以只进行一次ifelse测试,如果是FALSE,则将值NA传递给列表的相应元素,如果为TRUE,则为TRUE ,为列表中的每个元素评估不同的表达式?

注意:我的目标是减少system.time(系统和已过去)。如果这种修改不会减少时间和计算,请记住我有7200万次观察,这是一个可以接受的答案。我也欢迎改变代码其他部分的建议。

编辑:summaryRprof()

的结果
$by.total
                          total.time total.pct self.time self.pct
"system.time"                  18.94     99.79      0.00     0.00
".Call"                        18.92     99.68      0.10     0.53
"["                            18.92     99.68      0.04     0.21
"[.data.table"                 18.92     99.68      0.02     0.11
"eval"                         18.80     99.05      0.24     1.26
"ifelse"                       18.30     96.42      0.46     2.42
"lm"                           17.70     93.26      0.58     3.06
"sapply"                        8.06     42.47      0.36     1.90
"model.frame"                   7.74     40.78      0.16     0.84
"model.frame.default"           7.58     39.94      0.98     5.16
"lapply"                        6.62     34.88      0.70     3.69
"FUN"                           4.24     22.34      1.10     5.80
"model.matrix"                  4.04     21.29      0.02     0.11
"model.matrix.default"          4.02     21.18      0.26     1.37
"match"                         3.66     19.28      0.86     4.53
".getXlevels"                   3.12     16.44      0.12     0.63
"na.omit"                       2.40     12.64      0.24     1.26
"%in%"                          2.30     12.12      0.34     1.79
"simplify2array"                2.24     11.80      0.12     0.63
"na.omit.data.frame"            2.16     11.38      0.14     0.74
"[.data.frame"                  2.12     11.17      1.18     6.22
"deparse"                       1.80      9.48      0.66     3.48
"unique"                        1.80      9.48      0.54     2.85
"[["                            1.52      8.01      0.12     0.63
"[[.data.frame"                 1.40      7.38      0.54     2.85
".deparseOpts"                  1.34      7.06      0.96     5.06
"paste"                         1.32      6.95      0.16     0.84
"lm.fit"                        1.20      6.32      0.64     3.37
"mode"                          1.14      6.01      0.14     0.74
"unlist"                        1.12      5.90      0.56     2.95

1 个答案:

答案 0 :(得分:4)

而不是像这样形成和操作数据子集:

setkey(c,MM)
myresults <- c[, .SD[,{setkey=CO; eval(mycalc)},by=CO], by=MM]

您可以尝试这样做:

setkeyv(c, c("MM", "CO"))
myresults <- c[, eval(mycalc), by=key(c)]

这可以加速你的代码,因为它避免了.SD对象的所有嵌套子集,每个对象都需要自己调用[.data.table


在您的原始问题上,我怀疑ifelse评估需要花费很多时间,但如果您想避开它们,可以将它们从mycalc中取出并使用:=覆盖NA所需的值:

mycalc <- quote(list(MKTCAP = tail(SH,n=1)*tail(PRC,n=1),
                      SQSUM = sum(DAT^2,na.rm=TRUE),
                      COVCOMP = head(DAT,n=1),
                      NOBS = length(DAT[complete.cases(DAT)]))) 
setkeyv(c, c("MM", "CO"))
myresults <- c[, eval(mycalc), by=key(c)]


myresults[NOBS<2, c("SQSUM", "COVCOMP"):=NA_real_, with=FALSE]
## Or, alternatively
# myresults[NOBS<2, SQSUM:=NA_real_]
# myresults[NOBS<2, COVCOMP:=NA_real_]