Home > database >  How to write a function with an unspecified number of arguments where the arguments are column names
How to write a function with an unspecified number of arguments where the arguments are column names

Time:06-06

I am trying to write a function with an unspecified number of arguments using ... but I am running into issues where those arguments are column names. As a simple example, if I want a function that takes a data frame and uses within() to make a new column that is several other columns pasted together, I would intuitively write it as

example.fun <- function(input,...){
  res <- within(input,pasted <- paste(...))
  res}

where input is a data frame and ... specifies column names. This gives an error saying that the column names cannot be found (they are treated as objects). e.g.

df <- data.frame(x = c(1,2),y=c("a","b"))
example.fun(df,x,y)

This returns "Error in paste(...) : object 'x' not found "

I can use attach() and detach() within the function as a work around,

example.fun2 <- function(input,...){
  attach(input)
  res <- within(input,pasted <- paste(...))
  detach(input)
  res}

This works, but it's clunky and runs into issues if there happens to be an object in the global environment that is called the same thing as a column name, so it's not my preference.

What is the correct way to do this?

Thanks

CodePudding user response:

1) Wrap the code in eval(substitute(...code...)) like this:

example.fun <- function(data, ...) {
  eval(substitute(within(data, pasted <- paste(...))))
}

# test
df <- data.frame(x = c(1, 2), y = c("a", "b"))
example.fun(df, x, y)
##   x y pasted
## 1 1 a    1 a
## 2 2 b    2 b

1a) A variation of that would be:

example.fun.2 <- function(data, ...) {
  data.frame(data, pasted = eval(substitute(paste(...)), data))
}
example.fun.2(df, x, y)

2) Another possibility is to convert each argument to a character string and then use indexing.

example.fun.3 <- function(data, ...) {
  vnames <- sapply(substitute(list(...))[-1], deparse)
  data.frame(data, pasted = do.call("paste", data[vnames]))
}
example.fun.3(df, x, y)

3) Other possibilities are to change the design of the function and pass the variable names as a formula or character vector.

example.fun.4 <- function(data, formula) {
  data.frame(data, pasted = do.call("paste", get_all_vars(formula, data)))
}
example.fun.4(df, ~ x   y)

example.fun.5 <- function(data, vnames) {
  data.frame(data, pasted = do.call("paste", data[vnames]))
}
example.fun.5(df, c("x", "y"))
  • Related