私は集計関数をラップする方法を模索していますが(実際にはどのような関数でも可能です)、data.table(1つのdplyrの例もあります)とr data.table言語上の関数プログラミング/メタプログラミング/コンピューティング
- パフォーマンス(data.table潜在的な最適化に関しては、実装事項が適用される場合がありますん)
- 可読性(に関して関数型プログラミング/メタプログラミングのためのベストプラクティスに思ってすることはで一般的に合意された標準などがありましたほとんどのパッケージはdata.tableを利用しています)
- 容易性neralization基本的なアプリケーションは、寸法は、によって両方のそれぞれの得られた変数名を集約するために、柔軟テーブルを集約すなわち凝集する変数をパラメータ化することである
(方法メタプログラミングの違いは、「一般化」があります)および集約関数。
- fn_dt_agg1(ここで私は集計関数をパラメータ化方法を見つけ出すことができませんでした)
- @jangoreckiに触発fn_dt_agg2( ':私は(ほぼ)data.table 3と1つのdplyrの方法で同じ機能を実装していますsの彼は、「言語上のコンピューティング」と呼んで答えhere)
- メタプログラミングの別のアプローチであるように思わ@Arunの答えhereに触発fn_dt_agg3()
- fn_df_agg1(dplyrで同じの私の謙虚なアプローチ)
ライブラリ
library(data.table)
library(dplyr)
データ
n_size <- 1*10^6
sample_metrics <- sample(seq(from = 1, to = 100, by = 1), n_size, rep = T)
sample_dimensions <- sample(letters[10:12], n_size, rep = T)
df <-
data.frame(
a = sample_metrics,
b = sample_metrics,
c = sample_dimensions,
d = sample_dimensions,
x = sample_metrics,
y = sample_dimensions,
stringsAsFactors = F)
dt <- as.data.table(df)
実装
1 fn_dt_agg1
fn_dt_agg1 <-
function(dt, metric, metric_name, dimension, dimension_name) {
temp <- dt[, setNames(lapply(.SD, function(x) {sum(x, na.rm = T)}),
metric_name),
keyby = dimension, .SDcols = metric]
temp[]
}
res_dt1 <-
fn_dt_agg1(
dt = dt, metric = c("a", "b"), metric_name = c("a", "b"),
dimension = c("c", "d"), dimension_name = c("c", "d"))
2 fn_dt_agg2
fn_dt_agg2 <-
function(dt, metric, metric_name, dimension, dimension_name,
agg_type) {
j_call = as.call(c(
as.name("."),
sapply(setNames(metric, metric_name),
function(var) as.call(list(as.name(agg_type),
as.name(var), na.rm = T)),
simplify = F)
))
dt[, eval(j_call), keyby = dimension][]
}
res_dt2 <-
fn_dt_agg2(
dt = dt, metric = c("a", "b"), metric_name = c("a", "b"),
dimension = c("c", "d"), dimension_name = c("c", "d"),
agg_type = c("sum"))
all.equal(res_dt1, res_dt2)
#TRUE
3 fn_dt_agg3
fn_dt_agg3 <-
function(dt, metric, metric_name, dimension, dimension_name, agg_type) {
e <- eval(parse(text=paste0("function(x) {",
agg_type, "(", "x, na.rm = T)}")))
temp <- dt[, setNames(lapply(.SD, e),
metric_name),
keyby = dimension, .SDcols = metric]
temp[]
}
res_dt3 <-
fn_dt_agg3(
dt = dt, metric = c("a", "b"), metric_name = c("a", "b"),
dimension = c("c", "d"), dimension_name = c("c", "d"),
agg_type = "sum")
all.equal(res_dt1, res_dt3)
#TRUE
4 fn_df_agg1
fn_df_agg1 <-
function(df, metric, metric_name, dimension, dimension_name, agg_type) {
all_vars <- c(dimension, metric)
all_vars_new <- c(dimension_name, metric_name)
dots_group <- lapply(dimension, as.name)
e <- eval(parse(text=paste0("function(x) {",
agg_type, "(", "x, na.rm = T)}")))
df %>%
select_(.dots = all_vars) %>%
group_by_(.dots = dots_group) %>%
summarise_each_(funs(e), metric) %>%
rename_(.dots = setNames(all_vars, all_vars_new))
}
res_df1 <-
fn_df_agg1(
df = df, metric = c("a", "b"), metric_name = c("a", "b"),
dimension = c("c", "d"), dimension_name = c("c", "d"),
agg_type = "sum")
all.equal(res_dt1, as.data.table(res_df1))
#"Datasets has different keys. 'target': c, d. 'current' has no key."
ベンチマーキング
私はベンチマークの専門家ではないが、パフォーマンスに関する問題を明らかにしている可能性のある4つの実装すべてのベンチマークを実行した私が一般的に合意したベストプラクティスを適用していない場合)。私はfn_dt_agg1が1つ少ないパラメータ(集約関数)を持つので最も速くなることを期待していましたが、それはかなりの影響を及ぼさないようです。私はまた、比較的遅いdplyr機能にも驚いていましたが、これは私の目的のために悪い設計選択のためかもしれません。
library(microbenchmark)
bench_res <-
microbenchmark(
fn_dt_agg1 =
fn_dt_agg1(
dt = dt, metric = c("a", "b"),
metric_name = c("a", "b"),
dimension = c("c", "d"),
dimension_name = c("c", "d")),
fn_dt_agg2 =
fn_dt_agg2(
dt = dt, metric = c("a", "b"),
metric_name = c("a", "b"),
dimension = c("c", "d"),
dimension_name = c("c", "d"),
agg_type = c("sum")),
fn_dt_agg3 =
fn_dt_agg3(
dt = dt, metric = c("a", "b"),
metric_name = c("a", "b"),
dimension = c("c", "d"),
dimension_name = c("c", "d"),
agg_type = c("sum")),
fn_df_agg1 =
fn_df_agg1(
df = df, metric = c("a", "b"), metric_name = c("a", "b"),
dimension = c("c", "d"), dimension_name = c("c", "d"),
agg_type = "sum"),
times = 100L)
bench_res
# Unit: milliseconds
# expr min lq mean median uq max neval
# fn_dt_agg1 28.96324 30.49507 35.60988 32.62860 37.43578 140.32975 100
# fn_dt_agg2 27.51993 28.41329 31.80023 28.93523 33.17064 84.56375 100
# fn_dt_agg3 25.46765 26.04711 30.11860 26.64817 30.28980 153.09715 100
# fn_df_agg1 88.33516 90.23776 97.84826 94.28843 97.97154 172.87838 100
他のリソース
- Advanced R by Hadley Wickham: Expressions
- Advanced R by Hadley Wickham: Functions
- CRAN R Language Definition: Computing on the language
- CRAN Non-standard evaluation
- Data.table FAQ: Programmatically passing expressions in j
- Data.table meta-programming
- R data.table join: SQL select alike syntax in joined tables?
- Dynamically build call for lookup multiple columns
- Fast data.table assign of multiple columns by group from lookup
- How can one work fully generically in data.table in R with column names in variables
- Using get inside lapply, inside a function
re:agg2 "彼は「言語上のコンピューティング」と呼んでいます - 私はそうではありませんが、あなたが底にリンクしている公式の定義です。 – jangorecki