Home > OS >  perform custom summarise function by group in R
perform custom summarise function by group in R

Time:07-09

this is my first time posting a question here so be easy on me, let me know if you have tips for making my questions clearer.

I'm trying to initiate a function that will summarize given columns by group ("c", "e"), which I've initialized as shown below, but the output seems to ignore the grouping factor when I pass the parameters into the function (df, x). How can I ensure that grouping is respected when applying the custom summary function?

#initialize and relevel factor
dexadf$group <- factor(dexadf$group, levels=c("c", "e"),
                       labels = c("c", "e"))
dexadf$group <- relevel(dexadf$group, ref="c")
attributes(dexadf$group)

My data looks like this, I've only included 1 of the columns of interest (fm_bdc3) for sake of simplicity:

> dput(dexadf)
structure(list(participant = c("pt04", "pt75", "pt21", "pt73", 
"pt27", "pt39", "pt43", "pt52", "pt69", "pt49", "pt50", "pt56", 
"pt62", "pt68", "pt22", "pt64", "pt54", "pt79", "pt36", "pt26", 
"pt65", "pt38"), group = structure(c(1L, 2L, 2L, 1L, 1L, 2L, 
1L, 2L, 1L, 2L, 2L, 1L, 2L, 1L, 1L, 2L, 2L, 1L, 2L, 1L, 2L, 1L
), .Label = c("c", "e"), class = "factor"),  
    fm_bdc3 = c(18.535199635968, 23.52996574649, 17.276246451976, 
    11.526088555461, 23.805048656112, 23.08597823716, 28.691020942436, 
    28.968097858499, 23.378093165331, 22.491725344661, 14.609015054932, 
    19.734914019306, 31.947412973684, 25.152298171274, 12.007356801787, 
    20.836128108938, 22.322230884349, 14.777652101515, 21.389572717608, 
    16.992853675086, 14.138189878472, 17.777235203826)

→ function:

summbygrp <- function(df, x) {
        group_by(df, group) %>%
            summarise(
              count = n(),
              mean = mean(x, na.rm = TRUE),
              sd = sd(x, na.rm = TRUE)
            ) %>%
            mutate(se = sd / sqrt(11),
                   lower.ci = mean - qt(1 - (0.05 / 2), 11 - 1) * se,
                   upper.ci = mean   qt(1 - (0.05 / 2), 11 - 1) * se
                  )
      }

→ function output:

> summbygrp(dexadf, fm_bdc3) 
# A tibble: 2 × 7
  group count  mean    sd    se lower.ci upper.ci
  <fct> <int> <dbl> <dbl> <dbl>    <dbl>    <dbl>
1 c        11  20.6  5.48  1.65     16.9     24.3
2 e        11  20.6  5.48  1.65     16.9     24.3

As you can see, the summaries of both groups are identical, and I know this not to be true. Can someone identify the error in my code?

Here is the output if I don't use a function, but I have many columns so this would be pretty tedious to create for each column

group_by(dexadf, group) %>%
    summarise(
      count = n(),
      mean = mean(fm_bdc3, na.rm = TRUE),
      sd = sd(fm_bdc3, na.rm = TRUE)
    ) %>%
    mutate(se = sd / sqrt(11),
           lower.ci = mean - qt(1 - (0.05 / 2), 11 - 1) * se,
           upper.ci = mean   qt(1 - (0.05 / 2), 11 - 1) * se
    )

→ correct ouput:

# A tibble: 2 × 7
  group count  mean    sd    se lower.ci upper.ci
  <fct> <int> <dbl> <dbl> <dbl>    <dbl>    <dbl>
1 c        11  19.3  5.49  1.66     15.6     23.0
2 e        11  21.9  5.40  1.63     18.2     25.5

CodePudding user response:

library(dplyr)
library(rlang)


dexadf <- data.frame(
  stringsAsFactors = FALSE,
  participant = c("pt04","pt75","pt21","pt73",
                  "pt27","pt39","pt43","pt52","pt69","pt49","pt50",
                  "pt56","pt62","pt68","pt22","pt64","pt54","pt79",
                  "pt36","pt26","pt65","pt38"),
  fm_bdc3 = c(18.535199635968,23.52996574649,
              17.276246451976,11.526088555461,23.805048656112,
              23.08597823716,28.691020942436,28.968097858499,
              23.378093165331,22.491725344661,14.609015054932,19.734914019306,
              31.947412973684,25.152298171274,12.007356801787,
              20.836128108938,22.322230884349,14.777652101515,
              21.389572717608,16.992853675086,14.138189878472,17.777235203826),
  group = as.factor(c("c","e",
                      "e","c","c","e","c","e","c","e","e","c",
                      "e","c","c","e","e","c","e","c","e",
                      "c")),
  sex = as.factor(c("f","m",
                    "m","m","m","m","m","f","m","f","f","f",
                    "f","f","f","f","m","f","m","m","f",
                    "m"))
)


summbygrp <- function(df, x) {
  group_by(df, group) %>%
    summarise(
      count = n(),
      mean = mean({{x}}, na.rm = TRUE),
      sd = sd({{x}}, na.rm = TRUE)
    ) %>%
    mutate(se = sd / sqrt(11),
           lower.ci = mean - qt(1 - (0.05 / 2), 11 - 1) * se,
           upper.ci = mean   qt(1 - (0.05 / 2), 11 - 1) * se
    )
}

summbygrp(dexadf, fm_bdc3)

#> # A tibble: 2 × 7
#>   group count  mean    sd    se lower.ci upper.ci
#>   <fct> <int> <dbl> <dbl> <dbl>    <dbl>    <dbl>
#> 1 c        11  19.3  5.49  1.66     15.6     23.0
#> 2 e        11  21.9  5.40  1.63     18.2     25.5

Created on 2022-07-09 by the reprex package (v2.0.1)

Why this works

You actually need to use {{}}, pronounced as curly-curly, from rlang package to make this function work. When you want to pass varibales (i.e. columns of a dataset) as function parameters inside a function which uses dplyr or other tidyverse verbs (like mutate, summarise, group_by etc.), you need to wrap those parameter inside curly-curly like here we did with x. Otherwise the function won't work as intended and most probably throw erros. Because tidyverse verbs uses NSE (Non-Standard Evaluation). To know more, check out this Programming with dplyr and also I would encourage you to read chapters 17-20 of the book Advanced R

CodePudding user response:

an add-on question:

could you also help with this forloop that I'm using to apply the function to all columns in dexadf? Regardless of my function working on it's own, if I use it in the forloop it returns an output for every column with the exact same problem as before.

summbygrp <- function(x) {
  group_by(dexadf, group) %>%
        summarise(
          count = n(),
          mean = mean({{x}}, na.rm = TRUE),
          sd = sd({{x}}, na.rm = TRUE)
        ) %>%
        mutate(se = sd / sqrt(11),
               lower.ci = mean - qt(1 - (0.05 / 2), 11 - 1) * se,
               upper.ci = mean   qt(1 - (0.05 / 2), 11 - 1) * se
              ) 
} 

coln = 1

for (col in dexadf) {
  print(colnames(dexadf)[coln])
  if(is.numeric(col)) {
  print(summbygrp(col))
  } else {next}
  coln = coln   1
}

example outputs:

[1] "legslsm_percchg"
# A tibble: 2 × 7
  group count   mean    sd    se lower.ci upper.ci
  <fct> <int>  <dbl> <dbl> <dbl>    <dbl>    <dbl>
1 c        11 -0.286 0.732 0.221   -0.778    0.206
2 e        11 -0.286 0.732 0.221   -0.778    0.206
[1] "trunklsm_bdc3"
# A tibble: 2 × 7
  group count    mean     sd     se lower.ci upper.ci
  <fct> <int>   <dbl>  <dbl>  <dbl>    <dbl>    <dbl>
1 c        11 -0.0126 0.0355 0.0107  -0.0365   0.0112
2 e        11 -0.0126 0.0355 0.0107  -0.0365   0.0112
  • Related