Преобразование сводной функции в нестандартную оценку NSE в dplyr

Рассмотрим следующий интерактивный пример, который генерирует сводную таблицу:

library(dplyr)

tg <- ToothGrowth
ci_int <- 0.95

tg %>%
  group_by(supp, dose) %>%
  summarise(N = n(),
            mean = mean(len, na.rm = T),
            sd = sd(len, na.rm = T),
            se = sd / sqrt(N),
            ci = se * qt(ci_int / 2 + 0.50, N - 1))

#     supp  dose     N  mean       sd        se       ci
#   (fctr) (dbl) (int) (dbl)    (dbl)     (dbl)    (dbl)
# 1     OJ   0.5    10 13.23 4.459709 1.4102837 3.190283
# 2     OJ   1.0    10 22.70 3.910953 1.2367520 2.797727
# 3     OJ   2.0    10 26.06 2.655058 0.8396031 1.899314
# 4     VC   0.5    10  7.98 2.746634 0.8685620 1.964824
# 5     VC   1.0    10 16.77 2.515309 0.7954104 1.799343
# 6     VC   2.0    10 26.14 4.797731 1.5171757 3.432090

Я хотел бы преобразовать это в функцию и абстрагироваться от переменных data.frame, measure, groupvars группирующих переменных и conf.int. Вот начало:

library(lazyeval)

summarySE <- function(df, measure, groupvars, conf.int = 0.95) {
  summary_dots <- list(
    ~ n(), 
    interp(~ mean(var, na.rm = T), var = as.name(measure)),
    interp(~ sd(var, na.rm = T), var = as.name(measure))
  )

  df %>%
    group_by_(.dots = groupvars) %>%
    summarise_(.dots = setNames(summary_dots, c("N", "mean", "sd")))
}

summarySE(tg, "len", c("supp", "dose"))

Который дает:

#     supp  dose     N  mean       sd
#   (fctr) (dbl) (int) (dbl)    (dbl)
# 1     OJ   0.5    10 13.23 4.459709
# 2     OJ   1.0    10 22.70 3.910953
# 3     OJ   2.0    10 26.06 2.655058
# 4     VC   0.5    10  7.98 2.746634
# 5     VC   1.0    10 16.77 2.515309
# 6     VC   2.0    10 26.14 4.797731

Однако это не кажется очень СУХИМ? Кроме того, я не уверен, как реализовать se и ci, не становясь слишком сложным / многословным? Может быть, есть лучший подход или, возможно, его следует разделить на несколько функций?

Как преобразовать приведенную выше сводную таблицу в функцию, чтобы передать ей любую комбинацию data.frame с разными measure и groupvars с "духом" dplyr?


person JasonAizkalns    schedule 23.03.2016    source источник


Ответы (2)


Я действительно не совсем понимаю, почему расчет SE и CI сложнее, чем то, что вы уже делали.

Я использовал ... аргументы, чтобы зафиксировать ваши аргументы группировки, так как это кажется немного более простым в использовании.

В целом я получаю следующую функцию:

summarySE <- function(.data, measure, ..., conf.int = 0.95) {
  dots <- lazyeval::lazy_dots(...)
  measure <- lazyeval::lazy(measure)

  summary_dots <- list(
    N = ~ n(),
    mean = lazyeval::interp(~ mean(var, na.rm = T), var = measure),
    sd = lazyeval::interp(~ sd(var, na.rm = T), var = measure),
    se = ~ sd / sqrt(N),
    ci = ~ se * qt(conf.int / 2 + 0.50, N - 1))

  .data <- dplyr::group_by_(.data, .dots = dots)
  dplyr::summarise_(.data, .dots = summary_dots)
}

Вы можете сделать это в версии для SE и NSE, если хотите (и как это может сделать Хэдли).

Использование:

summarySE(tg, len, supp, dose)

Source: local data frame [6 x 7]
Groups: supp [?]

    supp  dose     N  mean       sd        se       ci
  (fctr) (dbl) (int) (dbl)    (dbl)     (dbl)    (dbl)
1     OJ   0.5    10 13.23 4.459709 1.4102837 3.190283
2     OJ   1.0    10 22.70 3.910953 1.2367520 2.797727
3     OJ   2.0    10 26.06 2.655058 0.8396031 1.899314
4     VC   0.5    10  7.98 2.746634 0.8685620 1.964824
5     VC   1.0    10 16.77 2.515309 0.7954104 1.799343
6     VC   2.0    10 26.14 4.797731 1.5171757 3.432090
person Axeman    schedule 28.03.2016
comment
Да, я передавал supp и dose как вектор, c(supp, dose) - я всегда боялся использовать ... - есть ли быстрая альтернатива, чтобы передать их как group = c(...)? - person JasonAizkalns; 28.03.2016
comment
Вы избавитесь от lazy_dots. Затем используйте список для группирующих переменных и напрямую передайте его group_by_. Однако их придется процитировать. Например. summarySE(tg, len, list(~supp, ~dose)). Не могу понять, как лениво записать список простых имен в список ленивых объектов. - person Axeman; 28.03.2016

Я не уверен, что это больше соответствует духу dplyr, но вы также можете попробовать использовать строки для вычисления mean, sd и т. Д .:

summarySE <- function(df, measure, groupvars, conf.int = 0.95) {
  df %>% group_by_(.dots = groupvars)%>%
    summarise_(N="n()",
               mean = paste0("mean(",measure,", na.rm = T)"),
               sd = paste0("sd(",measure,", na.rm = T)"),
               se = "sd/sqrt(N)",
               ci = paste0("se * stats::qt(",conf.int," / 2 + 0.50, N - 1)"))
}

summarySE(tg, "len", c("supp", "dose"))

#    supp  dose     N  mean       sd        se       ci
#  (fctr) (dbl) (int) (dbl)    (dbl)     (dbl)    (dbl)
#1     OJ   0.5    10 13.23 4.459709 1.4102837 3.190283
#2     OJ   1.0    10 22.70 3.910953 1.2367520 2.797727
#3     OJ   2.0    10 26.06 2.655058 0.8396031 1.899314
#4     VC   0.5    10  7.98 2.746634 0.8685620 1.964824
#5     VC   1.0    10 16.77 2.515309 0.7954104 1.799343
#6     VC   2.0    10 26.14 4.797731 1.5171757 3.432090
person NicE    schedule 26.03.2016