Search code examples
rnon-standard-evaluation

In R, how do I modify a dataframe column in a list given a string name


I'm new to R. Thank you for your patience. I'm working with the survey package.

Background: I'm writing a function that loops through combinations of predictor and outcome variables (i.e., svyglm(outcome~predictor)) in a complex survey to output crude prevalence ratios. For each outcome/predictor combination, I want to first relevel the predictor within the survey design object to ensure the output ratios are all > 1.

Specific problem: Given the survey design object name, column name and reference level as strings, how do I tell R I want said column releveled.

prams16 is the name of the survey design object which includes a list of 9 items, variables is the analytic dataset (data frame) within the survey design object and mrace is a column in the variables DF.

These work:

prams16$variables$mrace <- relevel(prams16$variables$mrace, ref="White")
prams16[["variables"]]["mrace"] <- relevel(prams16$variables$mrace, ref="White")

However, when I try to construct references to prams16$variables$mrace or prams16[["variables"]]["mrace"] with strings, nothing seems to work.

Thanks!

EDIT: Requested reproducible example of problem.

myPredictor <- as.factor(c("Red","White","Black","Red","Green","Black","White","Black","Red","Green","Black"))
myOutcome <- c(1,0,1,0,1,0,1,0,1,0,1)
myDF <- tibble(myPredictor, myOutcome)
myOtherStuff <- c("etc","etc")
myObj <- list(myDF=myDF,myOtherStuff=myOtherStuff)


#These work...
myObj$myDF$myPredictor <- relevel(myObj$myDF$myPredictor, ref="White")
str(myObj$myDF$myPredictor) #"White" is now the referent level

myObj[["myDF"]]["myPredictor"] <- relevel(myObj$myDF$myPredictor, ref="Red")
str(myObj$myDF$myPredictor) #"Red" is now the referent level

#How to construct relevel assignment statement from strings?
anObj <- "myObj"
aPredictor <- "myPredictor"
aRef <- "Green"

#Produces error
as.name(paste0(anObj,"$myDF$",aPredictor)) <- relevel(as.name(paste0(anObj,"$myDF$",aPredictor)), ref=aRef)

Solution

  • Here's a way to solve this using expression arithmetic. Our task is to construct and evaluate the following expression:

    myObj$myDF[[aPredictor]] <- relevel( myObj$myDF[[aPredictor]], ref=aRef )
    

    Step 1: Convert the string "myObj" to a symbolic name:

    sObj <- rlang::sym(anObj)      # Option 1
    sObj <- as.name(anObj)         # Option 2
    

    Step 2: Construct the expression myObj$myDF[[aPredictor]]:

    e1 <- rlang::expr( (!!sObj)$myDF[[aPredictor]] )
    

    Here, we use !! to tell rlang::expr that we want to replace sObj with whatever symbol is stored inside that variable. Without !!, the expression would be sObj$myDF[[aPredictor]], which is not quite what we want.

    Step 3: Construct the target expression:

    e2 <- rlang::expr( !!e1 <- relevel(!!e1, ref=aRef) )
    

    As before, !! replaces e1 with whatever expression is stored inside it (i.e., what we constructed in Step 2).

    Step 4: Evaluate the expression and inspect the result:

    eval.parent(e2)
    
    ## The column is now correctly releveled to Green
    myObj$myDF$myPredictor
    #   [1] Red   White Black Red   Green Black White Black Red   Green Black
    #  Levels: Green Black Red White