0

I want to enter multiple variables into an R function, and I want to enter them all in the table1() function ==> something like this line tab<-table1(~ var1+var2+ var3+...+varN|group, data=data)

library(table1)
dataset<-data.frame(ID=c(1,1,2,2,3,3,4,4),group=c("gp1","gp2","gp1","gp2","gp1","gp2","gp1","gp2"),
                 col1=c(0,1,1,1,0,1,1,0),col2=c(0,0,1,1,1,1,0,0),col3=c(1,0,1,0,1,1,1,0))


print.f <- function(data,var1,...,group){
  tab<-table1(~ var1+...|group, data=data)
  tab
}

print.f(data,var1,var2,var3,group=group)

print.f(dataset,col1,col2,col3)

enter image description here

If I will have for example a dataset that contains more than 3 columns and I want to see their output, how could I enter all of them?

3
  • 3
    Okay, so what happened when you tried? What's the question? Commented Dec 7, 2021 at 17:19
  • @camile, how could I enter the number of variables that I want into table1()? I did an edit to the post to make my question more clear Commented Dec 7, 2021 at 17:24
  • adv-r.hadley.nz/functions.html#fun-dot-dot-dot Commented Dec 7, 2021 at 17:36

2 Answers 2

2

Create the formula as a character vector, convert to formula class and run table1. In the examples we show several ways of creating the same output using print.f or using table1 directly.

print.f <- function(data, ..., group) {
  v <- paste(c(...), collapse = "+")
  if (!missing(group)) v <- paste(v, "|", group)
  fo <- as.formula(paste("~", v))
  table1(fo, data = data)
}

library(table1)

print.f(dataset, "col1", "col2", "col3", group = "group")

print.f(dataset, c("col1", "col2", "col3"), group = "group")
print.f(dataset, grep("col", names(dataset), value = TRUE), group = "group")
print.f(dataset, names(dataset)[2:4], group = "group")

print.f(dataset[-1], ".", group = "group")
print.f(dataset, ". - ID", group = "group")

table1(~ . | group, dataset[-1])
table1(~ . - ID | group, dataset)
Sign up to request clarification or add additional context in comments.

5 Comments

Very nifty! For my part, I was intrigued by a language-oriented approach (rather than one with pasted strings), and in the process, I came up with the op_literal() function as seen above. Do you know if a similar function already exists? I wasn't able to track anything down, so I had to make my own.
@Greg, reformulate(c("a", "b", "c"))[[2]] gives the same output as op_literal(`+`, a, b, c)
Awesome! Is there a function that extends to any binary operator whatsoever, for an arbitrarily large set of operands? Or only with + for formulas?
I don't think so. reformulate is intended to be used with regression formulas.
Good to know! If I can make op_literal() play nicely with packaged operands like dplyr::`%>%` , which are fully qualified yet which cannot be coerced to a symbol, then I might actually have something here!
1

Here's a solution that manipulates language rather than strings. You and others might also find op_literal() useful in the future.

Solution

Helper: op_literal()

This helper function op_literal() actually manipulates the R language itself to repetitively use a binary operator like + across many operands...even though a binary operator typically accepts only two operands. Calling op_literal(`+`, w, x, y, z) will actually generate this expression here: w + x + y + z.

# Helper function to arbitrarily repeat a binary operation (like '+').
op_literal <- function(op, ...) {
    # Capture the operator as a symbol.
    op_sym <- rlang::ensym(op)
    # Count the operands.
    n_dots <- rlang::dots_n(...)
    
    # Recursive case: a binary operator cannot handle this many arguments.
    if(n_dots > 2) {
        # Split off the final operand.
        dots <- rlang::exprs(...)
        dots_last <- dots[[n_dots]]
        dots <- dots[-n_dots]
        
        # Perform recursion for the remaining operands.
        op_left <- rlang::inject(op_literal(
            op = !!op_sym,
            ... = !!!dots
        ))
        
        # Assemble recursive results into the full operation.
        substitute(op(op_left, dots_last))
    }
    # Base case: the binary operator can handle 2(-) arguments.
    else {
        substitute(op(...))
    }
}

Note

Since op_literal() generates an expression, you still need to evaluate it if you want the result:

op_exp <- op_literal(`+`, 1, 2, 3, 4)
op_exp
#> 1 + 2 + 3 + 4

eval(op_exp)
#> [1] 10

Custom Function: print.f()

Next, this custom print.f() then leverages op_literal() to assemble the formula:

# Your custom 'print.f()' function.
print.f <- function(data, var1, ..., group) {
    # Capture the core variables as symbols.
    group_var <- rlang::ensym(group)
    other_vars <- rlang::ensym(var1)
    
    # Count the additional variables.
    n_dots <- rlang::dots_n(...)
    
    # Append those other variables if they exist.
    if(n_dots > 0) {
        other_vars <- rlang::inject(op_literal(op = `+`, !!other_vars, ...))
    }
    
    # Assemble the formula.
    formula_exp <- rlang::inject(~ !!other_vars | !!group_var)
    
    # Generate the table according to that formula.
    table1::table1(
        formula_exp,
        data = data
    )
}

Result

Given your dataset reproduced here

dataset <- data.frame(
    ID = c(1, 1, 2, 2, 3, 3, 4, 4),
    group = c("gp1", "gp2", "gp1", "gp2", "gp1", "gp2", "gp1", "gp2"),
    col1 = c(0, 1, 1, 1, 0, 1, 1, 0),
    col2 = c(0, 0, 1, 1, 1, 1, 0, 0),
    col3 = c(1, 0, 1, 0, 1, 1, 1, 0)
)

your call to print.f()

print.f(dataset, col1, col2, col3, group = group)

should yield the following visualization: 4

Note

As it stands, you have defined the group parameter at the end of your function header. This means that if you try calling print.f() like so

print.f(data = dataset, var = col1, col2, col3, group)

then you will get an error: without the group = specification, that final variable gets lumped together with col2 and col3, all under the ... umbrella. This will generate a bad formula:

~ col1 + col2 + col3 + group | 

To avoid the pain of having to type out group = every time, you can simply relocate it before the ..., like so:

print.f <- function(data, group, var1, ...) {
  #                       ^^^^^

Once you've done so, the following call will work as you intended:

print.f(dataset, group, col1, col2, col3)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.