Search code examples
rdplyrpurrr

How can I create colum names in a custom function, pass it to dplyr's select and use purrr's pmap to recycle the custom function?


The following code is supposed to create a list, df_test_purrr, with two dataframes. The two dataframes should contain the columns vs, vs_txt and am, am_txt, respectively.

As a first step, the function fn_test takes an argument for a dataframe, dta, and an argument for a column, col. Using the function directly with the argument dta = df_mtcars, col = expr(vs) creates the dataframe df_test as expected.

# Libaries
library(dplyr)
library(purrr)

# Data
df_mtcars <- mtcars %>%
  dplyr::mutate(vs_txt = dplyr::case_when(vs == 1 ~ "ABC", T ~ "XYZ"),
                am_txt = dplyr::case_when(am == 1 ~ "TTT", T ~ "BBB"))

# Function for one column
fn_test <- function(dta, col) {
  {{dta}} %>% 
    dplyr::select( {{col}}, as.name( paste0( {{col}}, "_txt") ) )
}

# Result for one column
df_test <- fn_test( dta = df_mtcars, col = expr(vs) )

# Purrr
fn_test_purrr <- function() {
  purrr::pmap(list(
    list(df_mtcars, df_mtcars),
    list(expr(vs), expr(am))
  ), fn_test )
}

df_test_purrr <- fn_test_purrr()

Defining and calling the function fn_test_purrr gives the error:

Error in `instrument_base_errors()`:
! object 'vs' not found
Caused by error:
! object 'vs' not found
Run `rlang::last_error()` to see where the error occurred.

How can I make the function work?

Please note that the function fn_test_purrr has more than two arguments in my real-world application such that I must use purrr's pmap (I guess).


Solution

  • Please check updated code which returns the list with 2 dataframes

    df_mtcars <- mtcars %>%
      dplyr::mutate(vs_txt = dplyr::case_when(vs == 1 ~ "ABC", T ~ "XYZ"),
                    am_txt = dplyr::case_when(am == 1 ~ "TTT", T ~ "BBB"))
    
    fn_test <- function(dta, col) {
      col2 <- paste0(col,'_txt')
      col2 <- col2[col2!='c_txt']
    
      if (length(col)==1){
        len=1
        col2 <- paste0(col,'_txt')
      } else if(length(col)>1){
        len <- length(col)-1  
      }
      
      my.list <- list()
      
      for(i in 1:len){
      colx <- paste0({{col}})[i+1]
      colx2 <- col2[i]
      dat <- dta %>% 
        dplyr::select( {{colx}}, colx2) 
        
      my.list[[i]] <- dat
      }
      return(my.list)
    }
    
    # Result for one column
    mylist <- fn_test( dta = df_mtcars, col = expr(c(vs,am)) )
    
    df1 <- as.data.frame(mylist[1])
    df2 <- as.data.frame(mylist[2])