У меня нет важного варианта использования, но я хотел бы понять, как аккуратный eval и data.table могут работать вместе.
У меня есть рабочие альтернативные решения, поэтому меня больше всего интересует, почему, потому что я надеюсь лучше понять аккуратный eval в целом, который поможет мне в самых разных случаях использования.
Как заставить data.table + tidy eval работать с group by?
В следующих примерах я использовал разрабатываемую версию rlang.
Обновить
Я обновил свой исходный вопрос на основе ответа Стефана Ф и моих дальнейших исследований: я больше не думаю, что вставленный ~ является важной частью вопроса, поскольку он также присутствует в коде dplyr, но у меня есть конкретный код: data.table + group by + quo, почему я не понял, не работает.
# setup ------------------------------------
suppressPackageStartupMessages(library("data.table"))
suppressPackageStartupMessages(library("rlang"))
suppressPackageStartupMessages(library("dplyr"))
#> Warning: package 'dplyr' was built under R version 3.5.1
dt <- data.table(
num_campaign = 1:5,
id = c(1, 1, 2, 2, 2)
)
df <- as.data.frame(dt)
# original question ------------------------
aggr_expr <- quo(sum(num_campaign))
q <- quo(dt[, aggr := !!aggr_expr][])
e <- quo_get_expr(q)
e
#> dt[, `:=`(aggr, ~sum(num_campaign))][]
dt[, `:=`(aggr, ~sum(num_campaign))][]
#> Error in `[.data.table`(dt, , `:=`(aggr, ~sum(num_campaign))): RHS of assignment is not NULL, not an an atomic vector (see ?is.atomic) and not a list column.
eval_tidy(e, data = dt)
#> num_campaign id aggr
#> 1: 1 1 15
#> 2: 2 1 15
#> 3: 3 2 15
#> 4: 4 2 15
#> 5: 5 2 15
использование выражения вместо quo в этом случае нецелесообразно, так как переменные в пользовательском выражении могут не оцениваться в хорошей среде:
# updated question --------------------------------------------------------
aggr_dt_expr <- function(dt, aggr_rule) {
aggr_expr <- enexpr(aggr_rule)
x <- 2L
q <- quo(dt[, aggr := !!aggr_expr][])
eval_tidy(q, data = dt)
}
x <- 1L
# expression is evaluated with x = 2
aggr_dt_expr(dt, sum(num_campaign) + x)
#> num_campaign id aggr
#> 1: 1 1 17
#> 2: 2 1 17
#> 3: 3 2 17
#> 4: 4 2 17
#> 5: 5 2 17
aggr_dt_quo <- function(dt, aggr_rule) {
aggr_quo <- enquo(aggr_rule)
x <- 2L
q <- quo(dt[, aggr := !!aggr_quo][])
eval_tidy(q, data = dt)
}
x <- 1L
# expression is evaluated with x = 1
aggr_dt_quo(dt, sum(num_campaign) + x)
#> num_campaign id aggr
#> 1: 1 1 16
#> 2: 2 1 16
#> 3: 3 2 16
#> 4: 4 2 16
#> 5: 5 2 16
У меня явная проблема с использованием группы по:
# using group by --------------------------------
grouped_aggr_dt_expr <- function(dt, aggr_rule) {
aggr_quo <- enexpr(aggr_rule)
x <- 2L
q <- quo(dt[, aggr := !!aggr_quo, by = id][])
eval_tidy(q, data = dt)
}
# group by has effect but x = 2 is used
grouped_aggr_dt_expr(dt, sum(num_campaign) + x)
#> num_campaign id aggr
#> 1: 1 1 5
#> 2: 2 1 5
#> 3: 3 2 14
#> 4: 4 2 14
#> 5: 5 2 14
grouped_aggr_dt_quo <- function(dt, aggr_rule) {
aggr_quo <- enquo(aggr_rule)
x <- 2L
q <- quo(dt[, aggr := !!aggr_quo, by = id][])
eval_tidy(q, data = dt)
}
# group by has no effect
grouped_aggr_dt_quo(dt, sum(num_campaign) + x)
#> num_campaign id aggr
#> 1: 1 1 16
#> 2: 2 1 16
#> 3: 3 2 16
#> 4: 4 2 16
#> 5: 5 2 16
# using dplyr works fine ------------------------------------------------------------
grouped_aggr_df_quo <- function(df, aggr_rule) {
aggr_quo <- enquo(aggr_rule)
x <- 2L
q <- quo(mutate(group_by(df, id), !!aggr_quo))
eval_tidy(q)
}
grouped_aggr_df_quo(df, sum(num_campaign) + x)
#> # A tibble: 5 x 3
#> # Groups: id [2]
#> num_campaign id `sum(num_campaign) + x`
#> <int> <dbl> <int>
#> 1 1 1 4
#> 2 2 1 4
#> 3 3 2 13
#> 4 4 2 13
#> 5 5 2 13
Я понимаю, что извлечение выражений из запросов - это не способ работать с аккуратным eval, но я надеялся использовать его в качестве инструмента отладки: (пока не очень удачно)
# returning expression in quo for debugging --------------
grouped_aggr_dt_quo_debug <- function(dt, aggr_rule) {
aggr_quo <- enquo(aggr_rule)
x <- 2L
q <- quo(dt[, aggr := !!aggr_quo, by = id][])
quo_get_expr(q)
}
grouped_aggr_dt_quo_debug(dt, sum(num_campaign) + x)
#> dt[, `:=`(aggr, ~sum(num_campaign) + x), by = id][]
grouped_aggr_df_quo_debug <- function(df, aggr_rule) {
aggr_quo <- enquo(aggr_rule)
x <- 2L
q <- quo(mutate(group_by(df, id), !!aggr_quo))
quo_get_expr(q)
}
# ~ is inserted in this case as well so it is not the problem
grouped_aggr_df_quo_debug(df, sum(num_campaign) + x)
#> mutate(group_by(df, id), ~sum(num_campaign) + x)
Создано 12 августа 2018 г. пакетом REPEX (v0.2.0).
Исходная формулировка вопроса:
Почему вставлен ~ и почему это не проблема аккуратного eval, если это проблема с базовым eval и все находится в глобальной среде?
Этот пример получен из более реалистичного, но также более сложного варианта использования, в котором я получил неожиданные результаты.