Search code examples
rfunctionsubsetevaluationellipsis

Wrapper for a function relying on non-standard evaluation in R


I wrote a wrapper around ftable because I need to compute flat tables with frequency and percentage for many variables:

mytable <- function(...) {
    tab <- ftable(...,
                  exclude = NULL)
    prop <- prop.table(x = tab,
                       margin = 2) * 100
    bind <- cbind(as.matrix(x = tab),
                  as.matrix(x = prop))
    margin <- addmargins(A = bind,
                         margin = 1)
    round(x = margin,
          digits = 1)
}

mytable(formula = wool + tension ~ breaks,
        data = warpbreaks)

    A_L A_M A_H B_L B_M B_H   A_L   A_M   A_H   B_L   B_M   B_H
10    0   0   1   0   0   0   0.0   0.0  11.1   0.0   0.0   0.0
12    0   1   0   0   0   0   0.0  11.1   0.0   0.0   0.0   0.0
13    0   0   0   0   0   1   0.0   0.0   0.0   0.0   0.0  11.1
14    0   0   0   1   0   0   0.0   0.0   0.0  11.1   0.0   0.0
15    0   0   1   0   0   2   0.0   0.0  11.1   0.0   0.0  22.2
...
Sum   9   9   9   9   9   9 100.0 100.0 100.0 100.0 100.0 100.0

However, I can not use the subset argument from ftable with my function, nor with the minimal mytable <- function(...) ftable(...):

mytable(formula = wool + tension ~ breaks,
        data = warpbreaks,
        subset = breaks < 20)

 Error in eval(substitute(subset), data, env) : 
  ..3 used in an incorrect context, no ... to look in

I know I can subset in the data argument with data = warpbreaks[warpbreaks$breaks < 20, ] as a workaround, but I am looking to improve my knowledge of R. "Advanced R" helped me to understand that the error is due to non-standard evaluation, but I did not manage to correct my code.

So my questions are:

  • How can I tell R to look for breaks in warpbreaks ?
  • More generally, is there a more obvious base R way to compute flat tables with frequency and percentage in a vertical layout for both single and multiple variables ? (I can get a vertical layout for a single variable with mytable(x = warpbreaks$tension, row.vars = 1).)

Solution

  • With a function definition without ..., I get a different error:

    mytable <- function(formula,
                        data,
                        subset) ftable(formula = formula,
                                       data = data,
                                       subset = subset)
    
    mytable(formula = wool + tension ~ breaks,
            data = warpbreaks,
            subset = breaks < 20)
    
     Error in xj[i] : invalid subscript type 'closure'
    

    This error led me to ressources I havent found before.

    Some threads led me to:

    # function 1
    mytable <- function(...) {
        mc <- match.call()
        mc["exclude"] <- list(NULL)
        do.call(what = ftable,
                args = as.list(x = mc[-1]))
        #etc
    }
    

    The write.csv family and lm source code led me to:

    # function 2
    mytable <- function(...) {
        mc <- match.call()
        mc[[1]] <- quote(expr = ftable)
        mc["exclude"] <- list(NULL)
        eval(expr = mc)
        # etc
    }
    

    However, I am looking for pro and cons of both methods (function 1 and function 2), because I do not know if a method is to be favored. So far I just found that do.call might be slower.

    More importantly, these methods led my to another issue: I can not use my wrapper with lapply and with anymore.