potterzot
potterzot

Reputation: 648

Using dplyr and lazyeval with '...'

Simplifying Edit:

It comes down to this:

df = data.frame(a = 1:10)

#example function that takes optional arguments
mymean <- function(x, w = 1, s = 0) { s + mean(w * x) }

summarize_(df, m = "mean(a)")
#>     m
#> 1 5.5

summarize_(df, m = "mymean(a)")
#> Error: could not find function "mymean"

According to `vignette("nse") summarize_ must be given the formula syntax when using non-standard summarizing functions.

Ultimately, I want to be able to wrap summarize_ in a function like so:

my_summary <- function(df, x, ...) {
  summarize_(df, 
             m = "mean(a)",
             wm = "mymean(a, ...)" #gives error 
}

#Partial working function
my_summary <- function(df, x, ...) {
  summarize_(df, 
             m = "mean(a)", #works
             wm1 = interp(mymean(a), a = as.name(x) #works but doesn't  allow ... 
             wm2 = interp(mymean(a, b), 
                          .values=list(a = as.name(x),
                                       b = quote(...)),  #doesn't work
             wm3 = interp(mymean(a, ...), a = as.name(x) #doesn't work
}

A working function would allow me to call:

my_summary(df, a)
#> 5.5

my_summary(df, a, w=5, s=2)
#> 29.5

Upvotes: 10

Views: 428

Answers (2)

Konrad Rudolph
Konrad Rudolph

Reputation: 545588

Since the problem is passing ... to the function, one solution is to construct the call via call and do.call (yes, both):

library(dplyr)

df = data.frame(a = 1:10)

mymean = function(x, w = 1, s = 0)
    s + mean(w * x)

my_summary = function (df, x, ...) {
    x = as.name(substitute(x))
    mycall = do.call(call, c('mymean', quote(x), list(...)))
    summarize_(df,
               m = lazyeval::interp(~mean(x), x = x),
               w = lazyeval::lazy_(mycall, environment()))
}

my_summary(df, a)
#>     m   w
#> 1 5.5 5.5
my_summary(df, a, w = 5, s = 2)
#>     m    w
#> 1 5.5 29.5

Incidentally, the above also fixes passing the column name — I couldn’t get your code to work, and I don’t think it would work that way.

Upvotes: 6

potterzot
potterzot

Reputation: 648

This very hacky, horrible function does the trick, but what if mymean contains many optional arguments?

mymean <- function(x, w=1, s = 0) { s + mean(w * x) }

my_summarize <- function(df, x, ...) {
  vlist = list(...)
  vlist_names = names(vlist)
  if ("w" %in% vlist_names & "s" %in% vlist_names) {
    res = summarize_(df, m = interp(~mymean(a, w=b, s=c), 
                                    .values = list(a = as.name(x),
                                                   b = vlist$w,
                                                   c = vlist$s)))
  }
  else if ("w" %in% vlist_names) {
    res = summarize_(df, m = interp(~mymean(a, w=b), 
                                    .values = list(a = as.name(x),
                                                   b = vlist$w)))
  }
  else if ("s" %in% vlist_names) {
    res = summarize_(df, m = interp(~mymean(a, s=c), 
                                    .values = list(a = as.name(x),
                                                   c = vlist$s)))
  }
  else {
    res = summarize_(df, m = interp(~mymean(a), a = as.name(x)))
  }
  res    
}


df = data.frame(a = 1:10)

my_summarize(df, "a")
#>     m
#> 1 5.5
my_summarize(df, "a", s=5)
#>      m
#> 1 10.5
my_summarize(df, "a", w=2)
#>    m
#> 1 11
my_summarize(df, "a", w=2, s=5)
#>    m
#> 1 16

Upvotes: 0

Related Questions