стандартная оценка в dplyr: суммировать переменную, заданную в виде строки символов
Я хочу ссылаться на неизвестное имя столбца внутри summarise
. Стандартные функции оценки, введенные в dplyr 0.3
, позволяют ссылаться на имена столбцов с использованием переменных, но это не работает, когда вы вызываете функцию base
R внутри, например. a summarise
.
library(dplyr)
key <- "v3"
val <- "v2"
drp <- "v1"
df <- data_frame(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))
df выглядит так:
> df
Source: local data frame [5 x 3]
v1 v2 v3
1 1 6 A
2 2 7 A
3 3 8 A
4 4 9 B
5 5 10 B
Я хочу удалить v1, группу по v3 и суммировать v2 для каждой группы:
df %>% select(-matches(drp)) %>% group_by_(key) %>% summarise_(sum(val, na.rm = TRUE))
Error in sum(val, na.rm = TRUE) : invalid 'type' (character) of argument
Версия NSE select()
отлично работает, так как она может соответствовать символьной строке. Версия SE group_by()
отлично работает, поскольку теперь она может принимать переменные в качестве аргументов и оценивать их. Тем не менее, я не нашел способа добиться аналогичных результатов при использовании базовых функций R внутри функций dplyr
.
Вещи, которые не работают:
df %>% group_by_(key) %>% summarise_(sum(get(val), na.rm = TRUE))
Error in get(val) : object 'v2' not found
df %>% group_by_(key) %>% summarise_(sum(eval(as.symbol(val)), na.rm = TRUE))
Error in eval(expr, envir, enclos) : object 'v2' not found
Я проверил несколько связанных questions, но ни одно из предлагаемых решений для меня пока не работает.
Ответы
Ответ 1
С выпуском пакета rlang и обновлением 0.7.0 до dplyr это стало довольно просто.
Если вы хотите использовать символьную строку (например, "v1") в качестве имени переменной, вы просто:
- Преобразуйте строку в символ, используя
sym()
из пакета rlang
- В своем вызове функции напишите
!!
перед символом
Например, вы должны сделать следующее:
my_var <- "Sepal.Length"
my_sym <- sym(my_var)
summarize(iris, Mean = mean(!!my_sym))
Более компактно, вы можете объединить шаг преобразования строки в символ с sym()
и префиксом его с !!
при написании вызова функции.
Например, вы можете написать:
my_var <- "Sepal.Length"
summarize(iris, mean(!!sym(my_var)))
Чтобы вернуться к исходному примеру, вы можете сделать следующее:
library(rlang)
key <- "v3"
val <- "v2"
drp <- "v1"
df <- data_frame(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))
df %>%
# NOTE: we don't have to do anything to 'drp'
# since the matches() function expects a character string
select(-matches(drp)) %>%
group_by(!!sym(key)) %>%
summarise(sum(!!sym(val), na.rm = TRUE))
Альтернативный синтаксис
С выпуском rlang версии 0.4.0 вы можете использовать следующий синтаксис:
my_var <- "Sepal.Length"
my_sym <- sym(my_var)
summarize(iris, Mean = mean({{ my_sym }}))
Вместо того, чтобы писать !!my_sym
, вы можете написать {{ my_sym }}
. Это имеет то преимущество, что, возможно, является более понятным, но имеет тот недостаток, что вам нужно преобразовать строку в символ, прежде чем поместить ее в скобки. Например, вы можете написать !!sym(my_var)
, но не можете написать {{sym(my_var)}}
Дополнительные детали
Из всей официальной документации, объясняющей, как работает использование sym()
и !!
, они являются наиболее доступными:
виньетка dplyr: программирование с помощью dplyr
Раздел книги Хэдли Уикхема "Продвинутый R" по метапрограммированию
Ответ 2
Обратите внимание, что этот ответ не относится к dplyr >= 0.7.0
, а к предыдущим версиям.
[ dplyr 0.7.0
] имеет новый подход к нестандартной оценке (NSE), называемый tidyeval. Он подробно описан в vignette("programming")
.
dplyr
виньетка на нестандартной evalutation здесь полезна. Проверьте раздел "Микширование констант и переменных", и вы обнаружите, что можно использовать функцию interp
из пакета lazyeval
и "[u] se as.name
, если у вас есть строка символов, которая дает имя переменной":
library(lazyeval)
df %>%
select(-matches(drp)) %>%
group_by_(key) %>%
summarise_(sum_val = interp(~sum(var, na.rm = TRUE), var = as.name(val)))
# v3 sum_val
# 1 A 21
# 2 B 19
Ответ 3
Передайте аргументу .dots
список строк, строящих строки, используя paste
, sprintf
или используя строчную интерполяцию из пакета gsubfn через fn$list
вместо list
, как мы здесь делаем:
library(gsubfn)
df %>%
group_by_(key) %>%
summarise_(.dots = fn$list(mean = "mean($val)", sd = "sd($val)"))
даяние:
Source: local data frame [2 x 3]
v3 mean sd
1 A 7.0 1.0000000
2 B 9.5 0.7071068
Ответ 4
Новое обновление dplyr:
Новая функциональность dplyr может помочь в этом. Вместо строк для переменных, которые требуют нестандартной оценки, мы используем quoures quo()
. Мы отменяем цитирование с другой функцией !!
, Подробнее об этом смотрите в этой виньетке. Вам потребуется версия dplyr для разработчиков до полного выпуска.
library(dplyr) #0.5.0.9004+
key <- quo(v3)
val <- quo(v2)
drp <- "v1"
df <- data_frame(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))
df %>% select(-matches("v1")) %>%
group_by(!!key) %>%
summarise(sum(!!val, na.rm = TRUE))
# # A tibble: 2 × 2
# v3 'sum(v2, na.rm = TRUE)'
# <chr> <int>
# 1 A 21
# 2 B 19