Подтвердить что ты не робот

стандартная оценка в 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, но ни одно из предлагаемых решений для меня пока не работает.

4b9b3361

Ответ 1

С выпуском пакета rlang и обновлением 0.7.0 до dplyr это стало довольно просто.

Если вы хотите использовать символьную строку (например, "v1") в качестве имени переменной, вы просто:

  1. Преобразуйте строку в символ, используя sym() из пакета rlang
  2. В своем вызове функции напишите !! перед символом

Например, вы должны сделать следующее:

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() и !!, они являются наиболее доступными:

  1. виньетка dplyr: программирование с помощью dplyr

  2. Раздел книги Хэдли Уикхема "Продвинутый 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