user13782962
user13782962

Reputation: 31

Compare Means (ANOVA) by groups in R using dplyr

I have aggregate summary results (N, mean, sd) for survey-questions for different subgroups (e.g. by course, age-group, gender). I would like to identify those subgroups where statistically significant entries exist to be then able to probe the results further. Ideally this should all work within the process of prepping the data for a report in R Markdown using tidyverse / dplyr.

My data look like this:

> head(demo, 11)
# A tibble: 11 x 7
# Groups:   qid, subgroup [3]
     qid question subgroup name       N  mean    sd
   <int> <chr>    <chr>    <chr>  <dbl> <dbl> <dbl>
 1     1 noise     NA       total   214  3.65 1.03
 2     1 noise     course   A       11  4     0.77
 3     1 noise     course   B       47  3.55  1.16
 4     1 noise     course   C       31  3.29  1.24
 5     1 noise     course   D       40  3.8   0.85
 6     1 noise     course   E       16  3.38  1.09
 7     1 noise     course   F       11  3.55  1.13
 8     1 noise     course   G       25  4.12  0.73
 9     1 noise     course   H       25  3.68  0.85
10     1 noise     gender   f       120 3.65  1.07
11     1 noise     gender   m       93  3.67  0.98

What I want, is a new column, indicating TRUE if there is a statistically significant difference within an subgroup for a given question, FALSE if otherwise. Like sigdiff below:

     qid question subgroup name       N  mean    sd     sigdiff     
   <int> <chr>    <chr>    <chr>  <dbl> <dbl> <dbl>       <lgl>
 2     1 noise     course   A       11  4     0.77        FALSE
 3     1 noise     course   B       47  3.55  1.16        FALSE 
 4     1 noise     course   C       31  3.29  1.24        FALSE 
 5     1 noise     course   D       40  3.8   0.85        FALSE 
 6     1 noise     course   E       16  3.38  1.09        FALSE 
 7     1 noise     course   F       11  3.55  1.13        FALSE 
 8     1 noise     course   G       25  4.12  0.73        FALSE 
 9     1 noise     course   H       25  3.68  0.85        FALSE 

Now, a very neat way to approach this seemed to be to determine if there's a significant difference beetween any groups by adapting this approach based on the rpsychi package.

I failed, however to adapt this to apply to my grouped tibble. My (failing) approach was to try to simply call a function doing the ANOVA by dplyr's newish group_map:

if(!require(rpsychi)){install.packages("rpsychi")}
library(rpsychi)
if(!require(tidyverse)){install.packages("tidyverse")}
library(tidyverse)

#' function establishing significant difference
#' between survey answers within subgroups

anovagrptest <- function(grpsum){
  
      anovaresult <- ind.oneway.second(grpsum$mean, grpsum$sd, grpsum$N, sig.level = 0.05)
      
      # compare critical F Value
      fcrit <- qf(.95, anovaresult$anova.table$df[1], anovaresult$anova.table$df[2])
      if(anovaresult$anova.table$F[1] > fcrit){return(TRUE)
      }else{return(FALSE)}
    }

#' pass the subset of the data for the group to the function which 
#' "returns a list of results from calling .f on each group"

relquestions <- demo %>% 
  group_by(qid, subgroup) %>% 
  group_map(~ anovagrptest(.x))

The code aborts due to "error in delta.upper + dfb : non-numeric argument for binary operator". Ideas much appreciated.

Upvotes: 3

Views: 1149

Answers (1)

Martin Gal
Martin Gal

Reputation: 16978

I think your row with NA causes your problem. First of all: I don't think you need that mapping function (but to be honest I'm not 100 % sure).

demo %>% 
  select(-id) %>%
  group_by(qid, subgroup) %>%
  mutate(new_column = ind.oneway.second(mean, sd, N, sig.level = 0.05) %>%
           {qf(.95, .[["anova.table"]][["df"]][1], .[["anova.table"]][["df"]][2]) < .[["anova.table"]][["F"]][1]})

causes

Error: Problem with `mutate()` input `new_column`.
x non-numeric argument for binary operator
i Input `new_column` is ``%>%`(...)`.
i The error occured in group 3: qid = 1, subgroup = NA.
Run `rlang::last_error()` to see where the error occurred.

When I remove the row containing NA

demo %>% 
  select(-id) %>%
  group_by(qid, subgroup) %>%
  drop_na() %>%
  mutate(new_column = ind.oneway.second(mean, sd, N, sig.level = 0.05) %>%
           {qf(.95, .[["anova.table"]][["df"]][1], .[["anova.table"]][["df"]][2]) < .[["anova.table"]][["F"]][1]})

I get

# A tibble: 10 x 8
# Groups:   qid, subgroup [2]
     qid question subgroup name      N  mean    sd new_column
   <dbl> <chr>    <chr>    <chr> <dbl> <dbl> <dbl> <lgl>  
 1     1 noise    course   A        11  4     0.77 FALSE  
 2     1 noise    course   B        47  3.55  1.16 FALSE  
 3     1 noise    course   C        31  3.29  1.24 FALSE  
 4     1 noise    course   D        40  3.8   0.85 FALSE  
 5     1 noise    course   E        16  3.38  1.09 FALSE  
 6     1 noise    course   F        11  3.55  1.13 FALSE  
 7     1 noise    course   G        25  4.12  0.73 FALSE  
 8     1 noise    course   H        25  3.68  0.85 FALSE  
 9     1 noise    gender   f       120  3.65  1.07 FALSE  
10     1 noise    gender   m        93  3.67  0.98 FALSE  

Upvotes: 1

Related Questions