0

So I have this code that will be part of an application (the application will allow you to import a data frame and apply different functions to it).

#create a list of functions
choose_functions <- function(...){
  rlang::enexprs(...)
}


#apply the list of functions to a data frame
apply_functions <- function(df, functions_list) {
  for (i in seq_along(functions_list)) {
    fn <- functions_list[[i]]
    tryCatch(
      {
        df <- eval_tidy(fn)
      },
      error = function(e) {
        message(paste("Error in filter", i, ":", conditionMessage(e), "\n"))
        return(NULL)
      },
      warning = function(e) {
        message(paste("This filter caused a warning", i, ":", conditionMessage(e), "\n"))
        return(NULL)
      }
    )
  }
  return(df)
}

To create the list of functions, you do something like this:

l1<- choose_functions(replace(df, df =="a" , "b"), 
                filter(df, x==10), 
                replace(df, df =="d" , "e"))

If I pass l1 like argument to my function apply_function(), everitihing works well.

The thing is that I would like the app user to be able to write the functions they want to apply, in this way:

l1<- choose_functions(replace("a" , "b"), 
                filter(x==10), 
                replace("d" , "e"))

I have tried:

apply_functions <- function(df, functions_list) {
  for (i in seq_along(functions_list)) {
    fn <- functions_list[[i]]
    tryCatch(
      {
        df <- df %>% eval_tidy(fn) 
                 #or
        df <- eval_tidy(fn, data=df)
                 #or
        df<-eval_tidy(fn, data=list(.data[[df]]))
      },
      error = function(e) {
        message(paste("Error in filter", i, ":", conditionMessage(e), "\n"))
        return(NULL)
      },
      warning = function(e) {
        message(paste("This filter caused a warning", i, ":", conditionMessage(e), "\n"))
        return(NULL)
      }
    )
  }
  return(df)
}

I also tried to rewrite the code to work with rlang::exec(), I also tried to make lists with the names of the functions and lists with the arguments, nothing worked.

Thanks

M--
  • 25,431
  • 8
  • 61
  • 93
anria
  • 37
  • 4
  • It's easier to help you if you include a simple [reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) with sample input that can be used to test and verify possible solutions. What errors are you getting exactly? – MrFlick Mar 27 '23 at 15:25

2 Answers2

1

You would need to pipe the data into each of the functions. The easiest way would be to modify the expression to include the %>% operator. You can do that with

apply_functions <- function(df, functions_list) {
  for (i in seq_along(functions_list)) {
    fn <- functions_list[[i]]
    tryCatch(
      {
        df <- rlang::eval_tidy(rlang::expr(df %>% !!fn))
      },
      error = function(e) {
        message(paste("Error in filter", i, ":", conditionMessage(e), "\n"))
        return(NULL)
      },
      warning = function(e) {
        message(paste("This filter caused a warning", i, ":", conditionMessage(e), "\n"))
        return(NULL)
      }
    )
  }
  return(df)
}

This would allow the following to work

apply_functions(iris, 
  choose_functions(
    filter(Sepal.Length>7)
  )
)
#    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
# 1           7.1         3.0          5.9         2.1 virginica
# 2           7.6         3.0          6.6         2.1 virginica
# 3           7.3         2.9          6.3         1.8 virginica
# 4           7.2         3.6          6.1         2.5 virginica
# 5           7.7         3.8          6.7         2.2 virginica
# ...
MrFlick
  • 195,160
  • 17
  • 277
  • 295
1

I extended MrFlicks's approach by rewriting the definitions of filter and replace, so that they have the form you say you want.

library(rlang)
library(magrittr)
library(dplyr)
filter <- dplyr::filter
classic_replace <- replace

(df_ <- data.frame(x=9:12,
           y=rep(letters[2:1],2)))

# classic_replace(df_,
#                 df_=="a",
#                 "b")

replace <- function(df_,from,to){
  classic_replace(x=df_,
                  df_==from,
                  to)
}
# a little test 
replace(df_ = df_,
        "a",
        "b"        )

choose_functions <- function(...){
  rlang::enexprs(...)
}

l1<- choose_functions(replace("a" , "b"), 
                      filter(x==10), 
                      replace("d" , "e"))

apply_functions <- function(df, functions_list) {

  for (i in seq_along(functions_list)) {
    fn <- functions_list[[i]]
    tryCatch(
      {
        df <- rlang::eval_tidy(rlang::expr(df %>% !!fn))
      },
      error = function(e) {
        message(paste("Error in filter", i, ":", conditionMessage(e), "\n"))
        return(NULL)
      },
      warning = function(e) {
        message(paste("This filter caused a warning", i, ":", conditionMessage(e), "\n"))
        return(NULL)
      }
    )
  }
  return(df)
}

df_
apply_functions(df_,l1)
Nir Graham
  • 2,567
  • 2
  • 6
  • 10