Courvoisier
Courvoisier

Reputation: 993

functions in a dataframe

I would like to create a list of functions and put them in a dataframe. It makes it easier for me to manipulate the functions for the rest of my work. I want to put the ecdf and the quantile functions while grouping on a columns of characters/factors.

I did something like this:

library(tidyverse)
quantilef = function(x) {
  qf = function(p){
    return(quantile(x, probs = p))
  }
  return(qf)
}

ecdfdf = iris %>%
  group_by(Species) %>%
  summarise(ecdf_ = list(ecdf(Sepal.Length)),
            qf_ = list(quantilef(x = Sepal.Length)))

The ecdf works as expected:

> ecdfdf %>% mutate(p = map_dbl(.x = ecdf_, .f = ~.x(5)))
# A tibble: 3 x 4
  Species    ecdf_  qf_        p
  <fct>      <list> <list> <dbl>
1 setosa     <ecdf> <fn>    0.56
2 versicolor <ecdf> <fn>    0.06
3 virginica  <ecdf> <fn>    0.02

But the quantile one gives NA:

> ecdfdf %>% mutate(q10 = map_dbl(.x = qf_, .f = ~.x(0.5)))
# A tibble: 3 x 4
  Species    ecdf_  qf_      q10
  <fct>      <list> <list> <dbl>
1 setosa     <ecdf> <fn>      NA
2 versicolor <ecdf> <fn>      NA
3 virginica  <ecdf> <fn>      NA

I know that the quantilef works, because it works outside the dataframe:

> qfsl = quantilef(x = iris$Sepal.Length)
> qfsl(0.5)
50% 
5.8 

What is going on? How do i get around this?

Upvotes: 1

Views: 55

Answers (1)

Allan Cameron
Allan Cameron

Reputation: 174468

I think the problem is that when you are creating the list of functions for qf_, you are passing in a parameter, x which is lazily evaluated. When you go to call the function, the x no longer exists in the calling environment.

One way round this is to wrap x in a quosure which is evaluated at the point your function is called.

Here's a reprex:

library(tidyverse)

quantilef = function(x) {
  y <- rlang::new_quosure(x)
  qf <- function(p){
    return(quantile(rlang::eval_tidy(y), probs = p))
  }
  return(qf)
}

ecdfdf = iris %>%
  group_by(Species) %>%
  summarise(ecdf_ = list(ecdf(Sepal.Length)),
            qf_ = list(quantilef(x = Sepal.Length)))

ecdfdf %>% mutate(q10 = map_dbl(.x = qf_, .f = ~.x(0.5)))
#> # A tibble: 3 x 4
#>   Species    ecdf_  qf_      q10
#>   <fct>      <list> <list> <dbl>
#> 1 setosa     <ecdf> <fn>     5  
#> 2 versicolor <ecdf> <fn>     5.9
#> 3 virginica  <ecdf> <fn>     6.5

Created on 2020-04-14 by the reprex package (v0.3.0)

Upvotes: 3

Related Questions