Search code examples
rlapplysubstitutionmapplyxtabs

using (m)apply to substitute more than 1 variable iteratively into a function?


I have a dataframe with 10 columns for different demographic variables, and 38 columns for responses to 38 different questions from survey participants. to make a smaller dataframe that replicates the organization:

ID <- c("a", "b", "c", "d", "e")
Gender <- c("Male", "Female", "Male", "Female", "Male")
Pain_Level <- c("High", "Moderate", "Low", "High", "Moderate")
Q1 <- c("Agree", "Strongly Agree", "Disagree", "Neutral", "Agree")
Q2 <- c("Disagree", "Strongly Agree", "Disagree", "Neutral", "Agree")
Q3 <- c("Agree", "Strongly Disagree", "Disagree", "Neutral", "Agree")

df<- data.frame(ID, Gender, Pain_Level, Q1, Q2, Q3)

I want to apply function xtabs to each of the question columns (Q1-38) of the dataframe to get contingency tables for each question, per demographic variable. I was able to create a function that can do this for all 38 questions, but only for one demographic at a time using:

varlist <- names(select(df, starts_with("Q")))       
xtabs <- lapply(varlist, function(x) {               
  xtabs(substitute(~Pain_Level + i, list(i = as.name(x))), data = df)    
}                                                                              
)  

Basically, I'd like to be able to then iteratively substitute Pain Level in the above equation, with Gender, and then all my other demographic variables, so I end up with a list of contingency tables: 38 tables per each demographic, for each question.

I have tried fiddling around with mapply, things like:

demolist <- names(select(df,"Pain_Level", "Gender"))

myfun <- function(x,y) {
  xtabs(substitute(~j + i, list(i = as.name(y), j=as.name(x))), data=df)
        }

attempt1 <- mapply(myfun, demolist, MoreArgs = list(varlist))

attempt2 <- mapply(myfun, demolist, varlist)

But what ends up happening is... it won't go through every combination of demographic / question. So I feel like I need to use lapply somehow but haven't had much success there either.

I feel like I am missing something - like approaching this from a fundamentally wrong way... but I am pretty new to programming so am having difficulty troubleshooting / thinking of alternatives. Any help is greatly appreciated.


Solution

  • The Map/mapply loop over the corresponding elements (for that purpose, it needs the arguments to be of same length or else it can do recycling when there is a single element) and doesn't do all the combinations. If we need all combinations, either expand.grid or outer is needed

    c(outer(demolist, varlist, Vectorize(myfun)))
    

    Or using tidyverse

    library(dplyr)
    library(tidyr)
    library(purrr)
    crossing(demolist, varlist) %>% 
               pmap(~ myfun(..1, ..2))
    #[[1]]
    #        Q1
    #Gender   Agree Disagree Neutral Strongly Agree
    #  Female     0        0       1              1
    #  Male       2        1       0              0
    
    #[[2]]
    #        Q2
    #Gender   Agree Disagree Neutral Strongly Agree
    #  Female     0        0       1              1
    #  Male       1        2       0              0
    
    #[[3]]
    #        Q3
    #Gender   Agree Disagree Neutral Strongly Disagree
    #  Female     0        0       1                 1
    #  Male       2        1       0                 0
    
    #[[4]]
    #          Q1
    #Pain_Level Agree Disagree Neutral Strongly Agree
    #  High         1        0       1              0
    #  Low          0        1       0              0
    # Moderate     1        0       0              1
    
    #[[5]]
    #          Q2
    #Pain_Level Agree Disagree Neutral Strongly Agree
    #  High         0        1       1              0
    #  Low          0        1       0              0
    #  Moderate     1        0       0              1
    
    #[[6]]
    #          Q3
    #Pain_Level Agree Disagree Neutral Strongly Disagree
    #  High         1        0       1                 0
    #  Low          0        1       0                 0
    #  Moderate     1        0       0                 1
    

    where

    demolist <- c("Pain_Level", "Gender")