IVI*_*VIM 4 loops r function dplyr data.table
在评估data.table(vs. dplyr)的效用时,关键因素是在函数和循环中使用它的能力.
为此,我修改了这篇文章中使用的代码片段:data.table vs dplyr:一个做得好,另一个做不好或做得不好?这样,代替硬编码的数据集变量名称("钻石"数据集的"切割"和"价格"变量),它变得与数据集无关 - 切割n-paste准备好在任何函数或循环内使用(当我们事先不知道列名.
这是原始代码:
library(data.table)
dt <- data.table(ggplot2::diamonds)
dt[cut != "Fair", .(mean(price),.N), by = cut]
Run Code Online (Sandbox Code Playgroud)
这是与数据集无关的等价物:
dt <- data.table(diamonds)
nVarGroup <- 2 #"cut"
nVarMeans <- 7 #"price"
strGroupConditions <- levels(dt[[nVarGroup]])[-1] # "Good" "Very Good" "Premium" "Ideal"
strVarGroup <- names(dt)[nVarGroup]
strVarMeans <- names(dt)[nVarMeans]
qAction <- quote(mean(get(strVarMeans))) #! w/o get() it does not work!
qGroup <- quote(get(strVarGroup) %in% strGroupConditions) #! w/o get() it does not work!
dt[eval(qGroup), .(eval(qAction), .N), by = strVarGroup]
Run Code Online (Sandbox Code Playgroud)
注意(感谢下面的回复):如果您需要通过引用更改变量值,则需要使用(),而不是get(),如下所示:
strVarToBeReplaced <- names(dt)[1]
dt[eval(qGroup), (strVarToBeReplaced) := eval(qAction), by = strGroup][]
Run Code Online (Sandbox Code Playgroud)
现在:您可以为所有循环需求剪切以下代码:
for(nVarGroup in 2:4) # Grouped by several categorical values...
for(nVarMeans in 5:10) { # ... get means of all numerical parameters
strGroupConditions <- levels(dt[[nVarGroup]])[-1]
strVarGroup <- names(dt)[nVarGroup]
strVarMeans <- names(dt)[nVarMeans]
qAction <- quote(mean(get(strVarMeans)))
qGroup <- quote(get(strVarGroup) %in% strGroupConditions)
p <- dt[eval(qGroup), .(AVE=eval(qAction), COUNT=.N), by = strVarGroup]
print(sprintf("nVaGroup=%s, nVarMeans=%s: ", strVarGroup, strVarMeans))
print(p)
}
Run Code Online (Sandbox Code Playgroud)
我的第一个问题:
上面的代码虽然能够满足所需的功能/循环需求,但却显得非常复杂.-它使用不同的多个(可能是非一致)非直观招数这样的组合(),get(),quote()/ eval(),[[]]).对于这种直截了当的需求似乎太多了......
是否有另一种更好的方法来访问和修改循环中的data.tables值?也许用on=, lapply/ .SD/ .SDcols?
请在下面分享您的想法.本讨论旨在补充和整合其他帖子中的相关位(例如此处列出:如何在R中的data.table中完全一般地使用变量中的列名称).最终,创建一个专用的插图来使用data.table内部functions和内部会很棒loops.
第二个问题:
为此目的,dplyr更容易吗?- 但是对于这个问题,我已经设置了一个单独的帖子:dplyr比在函数和循环中使用的data.table更容易吗?.
OP已经要求与分组和聚合的数据集无关等效.
使用开发版本1.10.5,data.table获得了新的分组集功能:rollup(),cube()并且groupingsets()允许在不同的分组级别上聚合,同时产生小计和总计.
增加的抽象级别可用于与数据集无关的方法.for在OP的示例中使用双嵌套循环计算的小计也可以通过以下方式生成
library(data.table) # version 1.10.5 required
dt = data.table(ggplot2::diamonds)
groupingsets(dt, c(lapply(.SD, mean), list(COUNT = .N)),
by = names(dt)[2:4], .SDcols = 5:10, id = FALSE,
sets = as.list(names(dt)[2:4]))
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)cut color clarity depth table price x y z COUNT 1: Ideal NA NA 61.70940 55.95167 3457.542 5.507451 5.520080 3.401448 21551 2: Premium NA NA 61.26467 58.74610 4584.258 5.973887 5.944879 3.647124 13791 3: Good NA NA 62.36588 58.69464 3928.864 5.838785 5.850744 3.639507 4906 4: Very Good NA NA 61.81828 57.95615 3981.760 5.740696 5.770026 3.559801 12082 5: Fair NA NA 64.04168 59.05379 4358.758 6.246894 6.182652 3.982770 1610 6: NA E NA 61.66209 57.49120 3076.752 5.411580 5.419029 3.340689 9797 7: NA I NA 61.84639 57.57728 5091.875 6.222826 6.222730 3.845411 5422 8: NA J NA 61.88722 57.81239 5323.818 6.519338 6.518105 4.033251 2808 9: NA H NA 61.83685 57.51781 4486.669 5.983335 5.984815 3.695965 8304 10: NA F NA 61.69458 57.43354 3724.886 5.614961 5.619456 3.464446 9542 11: NA G NA 61.75711 57.28863 3999.136 5.677543 5.680192 3.505021 11292 12: NA D NA 61.69813 57.40459 3169.954 5.417051 5.421128 3.342827 6775 13: NA NA SI2 61.77217 57.92718 5063.029 6.401370 6.397826 3.948478 9194 14: NA NA SI1 61.85304 57.66254 3996.001 5.888383 5.888256 3.639845 13065 15: NA NA VS1 61.66746 57.31515 3839.455 5.572178 5.581828 3.441007 8171 16: NA NA VS2 61.72442 57.41740 3924.989 5.657709 5.658859 3.491478 12258 17: NA NA VVS2 61.66378 57.02499 3283.737 5.218454 5.232118 3.221465 5066 18: NA NA VVS1 61.62465 56.88446 2523.115 4.960364 4.975075 3.061294 3655 19: NA NA I1 62.73428 58.30378 3924.169 6.761093 6.709379 4.207908 741 20: NA NA IF 61.51061 56.50721 2864.839 4.968402 4.989827 3.061659 1790
因此,我们不必知道列的名称.但是,我们必须指定要分组的列和要聚合的列.
这可能不是最相似或最快的解决方案,但我会简化此特定data.table循环中的代码,如下所示:
for(nVarGroup in 2:4) { # Grouped by several categorical values...
for(nVarMeans in 5:10) { # ... get means of all numerical parameters
strGroupConditions <- levels(dt[[nVarGroup]])[-1]
strVarGroup <- names(dt)[nVarGroup]
strVarMeans <- names(dt)[nVarMeans]
# qAction <- quote(mean(get(strVarMeans)))
# qGroup <- quote(get(strVarGroup) %in% strGroupConditions)
# p <- dt[eval(qGroup), .(AVE = eval(qAction), COUNT = .N), by = strVarGroup]
setkeyv(dt, strVarGroup)
p <- dt[strGroupConditions, .(AVE = lapply(.SD, mean), COUNT = .N), by = strVarGroup,
.SDcols = strVarMeans]
print(sprintf("nVaGroup = %s, nVarMeans = %s", strVarGroup, strVarMeans))
print(p)
}
}
Run Code Online (Sandbox Code Playgroud)
我将旧代码留下作为注释以供参考。
qActionlapply(.SD, mean)通过与参数一起使用来替换.SDcols。
qGroup子集行被设置键和提供所需值的向量作为参数的组合所取代i。
如果是更复杂的子集表达式,我会尝试使用非等(或条件)连接on=语法使用非等(或条件)连接。
或者,按照Matt Dowle 的建议创建一个要计算的表达式,“类似于构建发送到服务器的动态 SQL 语句”。
马特建议创建一个辅助函数
EVAL <- function(...) eval(parse(text = paste0(...)), envir = parent.frame(2))
Run Code Online (Sandbox Code Playgroud)
正如G. Grothendieck 所建议的fn$,它可以与“包中的准 Perl 类型字符串插值相结合gsubfn,以提高 EVAL 解决方案的可读性” 。
这样,循环的代码最终变成:
EVAL <- function(...) eval(parse(text = paste0(...)), envir = parent.frame(2))
library(gsubfn)
for(nVarGroup in 2:4) { # Grouped by several categorical values...
for(nVarMeans in 5:10) { # ... get means of all numerical parameters
strGroupConditions = levels(dt[[nVarGroup]])[-1]
strVarGroup = names(dt)[nVarGroup]
strVarMeans = names(dt)[nVarMeans]
p <- fn$EVAL("dt[$strVarGroup %in% strGroupConditions, .(AVE=mean($strVarMeans), COUNT=.N), by = strVarGroup]" )
print(sprintf("nVaGroup = %s, nVarMeans = %s", strVarGroup, strVarMeans))
print(p)
}
}
Run Code Online (Sandbox Code Playgroud)
现在,该data.table语句看起来非常像“本机”语句,除了$strVarGroupand$strVarMeans用于引用变量内容的地方。
在版本 1.1.0(CRAN 于 2016-08-19 发布)中,该stringr软件包增加了字符串插值功能str_interp(),这是此处软件包的替代方案gsubfn。
使用 时str_interp(),for 循环中的中心语句将变为
p <- EVAL(stringr::str_interp(
"dt[${strVarGroup} %in% strGroupConditions, .(AVE=mean(${strVarMeans}), COUNT=.N), by = strVarGroup]"
))
Run Code Online (Sandbox Code Playgroud)
library(gsubfn)并且可以删除对的调用。
| 归档时间: |
|
| 查看次数: |
2433 次 |
| 最近记录: |