Converting a summary function to non-standard evaluation NSE in dplyr

Viewed 308

Consider the following interactive example that generates a summary table:

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

I would like to convert this to a function and abstract away the data.frame, measure variable, groupvars grouping variables, and the conf.int. Here's a start:

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"))

Which yields:

#     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

However, this doesn't feel very DRY? In addition, I'm not sure how to implement se and ci without getting overtly complex/verbose? Perhaps there's a better approach altogether or perhaps this should be split up into several functions?

How can I convert the summary table above to a function so that I can pass it any combination of a data.frame with different measure and groupvars with the "spirit" of dplyr?

2 Answers
Related