Search code examples
rrlang

How to apply a list of expressions to a data frame using purrr::reduce()?


I saw this idea on another question here.

I created a list of functions using rlang::enexprs(). I want to apply this list on a data frame, so that I get a single frame with all the functions applied.

I have tried something like:

select.expr <- function(...) rlang::enexprs(...)

apply.expr <- function(df, filtre.list) { 
    eval_df <- function(df, expr) rlang::eval_tidy(expr, data = df)
    purrr::reduce(filtre.list, eval_df, .init = df) 
}

But it doesn’t work as I would like. For example:

data <- data.frame(a = c(1, 1, 1, 1),
                   b = c(2, 2, 2, 2),
                   c = c(3, 3, 3, 3))

expr <- select.expr(replace(data, data == 2, "two"), 
                    replace(data, data == 3, "three"))

apply.expr(data, expr)
##   a b     c
## 1 1 2 three
## 2 1 2 three
## 3 1 2 three
## 4 1 2 three

This only works if I pass my data frame data as an argument to eval_df:

apply.expr <- function(df, filtre.list) { 
    eval_df <- function(data, expr) rlang::eval_tidy(expr, data = data)
    purrr::reduce(filtre.list, eval_df, .init = df) 
}

apply.expr(data, expr)
##   a   b     c
## 1 1 two three
## 2 1 two three
## 3 1 two three
## 4 1 two three

But then I cannot use my function apply.expr() with another data frame. I have also tried with map(). Is there a way to create a function that applies a list of expressions to any date frame?


Solution

  • The issue is your expr alters data and in apply.expr() you are changing df. Therefore, we should change ```expr`` into:

    expr <- select.expr(replace(df, df== 2, "two"), 
                        replace(df, df== 3, "three"))
    

    which works as expected:

    apply.expr(data, expr)
      a   b     c
    1 1 two three
    2 1 two three
    3 1 two three
    4 1 two three
    

    and it works on another dataset:

    data_alt <- data.frame(a = c(1, 1, 1, 1),
                       b = c(2, 2, 2, 2),
                       c = c(3, 3, 3, 3))
    
    apply.expr(data_alt, expr)
      a   b     c
    1 1 two three
    2 1 two three
    3 1 two three
    4 1 two three