Passing a column name into R dplyr group_by and summarise function

Viewed 813

I am trying to write a function that takes a data frame and a variable name (or list of variable names) and outputs summary information using the group_by and summarize functions. However, I keep getting either the following error:

 Error: Problem with `mutate()` input `..1`.
 x Input `..1` must be a vector, not a function.
 i Input `..1` is `<fn>`.

or this error:

Error in (function (x)  : object 'ym' not found

The last error says it can't find the column named "value", which contains the data frame's values (after being melted).

Here is my code:

    tested <- melt(test_data, measure.vars = c('TA','PP','US','UD','UE','UG','UH','XR','RW','PA','TB4',
                                               'TV2','TV4','TV8','TV20','TV40','MV2','MV4','MV8','MV20','MV40','VB'), id.vars = c('TmStamp','year','month','ym','day','hour'))
    
    test_function <- function(data,col){
      stats <- data %>% group_by(!!col,variable) %>%
        summarize(N = length(value[!is.na(value)]),
                  Missing = length(value[is.na(value)]),
                  Per.Avail = (length(value[!is.na(value)])/(length(value[!is.na(value)]) + length(value[is.na(value)]))) * 100,
                  Mean = mean(value, na.rm=TRUE),
                  Median = median(value, na.rm=TRUE),
                  Min = min(value, na.rm=TRUE),
                  Max = max(value, na.rm=TRUE),
                  Range = max(value, na.rm=TRUE) - min(value, na.rm=TRUE),
                  Variance = var(value, na.rm=TRUE),
                  Std.Dev = sd(value, na.rm=TRUE),
                  Coef.Var = sd(value, na.rm=TRUE)/mean(value, na.rm=TRUE),
                  SE = sd(value, na.rm=TRUE)/sqrt(length(value[!is.na(value)])),
                  Skewness = e1071::skewness(value, na.rm=TRUE),
                  Kurtosis = e1071::kurtosis(value, na.rm=TRUE),
                  IQR = IQR(value, na.rm=TRUE),
                  MAD = mad(value, na.rm=TRUE)
        )
      return(stats)
    }

    test_function(tested, ym)

Here is a small data sample. Note that "variable" is a column which is always being passed into the group_by function, so I decided to hard code that in.

structure(list(TmStamp = c("2019-10-01 12:00:00 AM", "2019-10-01 12:05:00 AM", 
"2019-10-01 12:10:00 AM", "2019-10-01 12:15:00 AM", "2019-10-01 12:20:00 AM", 
"2019-10-01 12:25:00 AM", "2019-10-01 12:30:00 AM", "2019-10-01 12:35:00 AM", 
"2019-10-01 12:40:00 AM", "2019-10-01 12:45:00 AM", "2019-10-01 12:50:00 AM", 
"2019-10-01 12:55:00 AM", "2019-10-01 01:00:00 AM", "2019-10-01 01:05:00 AM", 
"2019-10-01 01:10:00 AM", "2019-10-01 01:15:00 AM", "2019-10-01 01:20:00 AM", 
"2019-10-01 01:25:00 AM", "2019-10-01 01:30:00 AM", "2019-10-01 01:35:00 AM"
), year = c(2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019
), month = c(10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 
10, 10, 10, 10, 10, 10, 10, 10), ym = c("10-2019", "10-2019", 
"10-2019", "10-2019", "10-2019", "10-2019", "10-2019", "10-2019", 
"10-2019", "10-2019", "10-2019", "10-2019", "10-2019", "10-2019", 
"10-2019", "10-2019", "10-2019", "10-2019", "10-2019", "10-2019"
), day = structure(c(18170, 18170, 18170, 18170, 18170, 18170, 
18170, 18170, 18170, 18170, 18170, 18170, 18170, 18170, 18170, 
18170, 18170, 18170, 18170, 18170), class = "Date"), hour = c(23L, 
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L), variable = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("TA", 
"PP", "US", "UD", "UE", "UG", "UH", "XR", "RW", "PA", "TB4", 
"TV2", "TV4", "TV8", "TV20", "TV40", "MV2", "MV4", "MV8", "MV20", 
"MV40", "VB"), class = "factor"), value = c(6.008, 6.013, 5.915, 
5.777, 5.727, 5.679, 5.653, 5.591, 5.479, 5.353, 5.299, 5.249, 
5.256, 5.171, 5.01, 4.901, 4.716, 4.487, 4.397, 4.25)), row.names = c(NA, 
20L), class = "data.frame")

How would I write this function so it accepts one or more column names in the group_by function?

1 Answers

To make your function work use e.g. {{col}} instead of !!col. To make your function work for multiple vars you can use the ... notation which you can also use to pass the variables to group_by:

library(dplyr)

test_function <- function(data, ...){
  stats <- data %>% 
    group_by(variable, ...) %>%
    summarize(N = length(value[!is.na(value)]),
              Missing = length(value[is.na(value)]),
              Per.Avail = (length(value[!is.na(value)])/(length(value[!is.na(value)]) + length(value[is.na(value)]))) * 100,
              Mean = mean(value, na.rm=TRUE),
              Median = median(value, na.rm=TRUE),
              Min = min(value, na.rm=TRUE),
              Max = max(value, na.rm=TRUE),
              Range = max(value, na.rm=TRUE) - min(value, na.rm=TRUE),
              Variance = var(value, na.rm=TRUE),
              Std.Dev = sd(value, na.rm=TRUE),
              Coef.Var = sd(value, na.rm=TRUE)/mean(value, na.rm=TRUE),
              SE = sd(value, na.rm=TRUE)/sqrt(length(value[!is.na(value)])),
              Skewness = e1071::skewness(value, na.rm=TRUE),
              Kurtosis = e1071::kurtosis(value, na.rm=TRUE),
              IQR = IQR(value, na.rm=TRUE),
              MAD = mad(value, na.rm=TRUE)
    )
  return(stats)
}

test_function(tested, ym)
#> `summarise()` regrouping output by 'variable' (override with `.groups` argument)
#> # A tibble: 1 x 18
#> # Groups:   variable [1]
#>   variable ym        N Missing Per.Avail  Mean Median   Min   Max Range Variance
#>   <fct>    <chr> <int>   <int>     <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl>    <dbl>
#> 1 TA       10-2~    20       0       100  5.30   5.33  4.25  6.01  1.76    0.283
#> # ... with 7 more variables: Std.Dev <dbl>, Coef.Var <dbl>, SE <dbl>,
#> #   Skewness <dbl>, Kurtosis <dbl>, IQR <dbl>, MAD <dbl>

test_function(tested, ym, year, month)
#> `summarise()` regrouping output by 'variable', 'ym', 'year' (override with `.groups` argument)
#> # A tibble: 1 x 20
#> # Groups:   variable, ym, year [1]
#>   variable ym     year month     N Missing Per.Avail  Mean Median   Min   Max
#>   <fct>    <chr> <dbl> <dbl> <int>   <int>     <dbl> <dbl>  <dbl> <dbl> <dbl>
#> 1 TA       10-2~  2019    10    20       0       100  5.30   5.33  4.25  6.01
#> # ... with 9 more variables: Range <dbl>, Variance <dbl>, Std.Dev <dbl>,
#> #   Coef.Var <dbl>, SE <dbl>, Skewness <dbl>, Kurtosis <dbl>, IQR <dbl>,
#> #   MAD <dbl>
Related