36

I'm sure this is simple, but I cannot find a solution ... I would like to use a variable containing a character string as argument for a function.

x <- c(1:10)
myoptions <- "trim=0, na.rm=FALSE"

Now, something like

foo <- mean(x, myoptions)

should be the same as

foo <- mean(x, trim=0, na.rm=FALSE)

Thanks in advance!

3
  • 1
    Although I am sure someone will post a solution to this, I think doing this is a bit unusual. Do you want to give a bit more background on what really you want to do? Commented Oct 20, 2011 at 13:59
  • I have a CSV file written by an external program which contains one string of arguments per line. This should be used as input for the svm() function to build SVM models with different settings. So the first line of the CSV file is "cost=1, gamma=0.001", the second line is "cost=5, gamma=0.001" and so on. I want to loop over all lines of the CSV file. Commented Oct 20, 2011 at 14:09
  • 1
    If you paste the first few line of the CSV file, probably you will get better answer. Commented Oct 20, 2011 at 14:25

4 Answers 4

40

You can use eval and parse:

foo <- eval(parse(text = paste("mean(x,", myoptions, ")")))
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much, this works! Perhaps my thought were in the wrong direction -- so there is no way to do it in a way like foo <- mean(x, take.this.as.arguments(myoptions)) ?
@Kilian it is difficult, maybe impossible.
I suspect that @koshke would no longer say this to be even difficult. I think that csgillespie's answer is the correct one.
14

A more natural way to do what you want is to use do.call. For example,

R> l[["trim"]] = 0
R> l[["na.rm"]] = FALSE
R> l[["x"]] = 1:10
##Or l <- list(trim = 0, na.rm = FALSE, x = 1:10)
R> do.call(mean, l)
 [1] 5.5

If for some reason you really want to use a myoptions string, you could always use strsplit to coarce it into a list form. For example,

R> y = "trim=0, na.rm=FALSE"
R> strsplit(y, ", ")
[[1]]
[1] "trim=0"      "na.rm=FALSE" 
R> strsplit(y, ", ")[[1]][1]
[1] "trim=0"

8 Comments

Is there a reason for not using l <- list(trim = 0, na.rm = FALSE, x = 1:10)?
I'm voting for the second method: do.call with strsplit.
@DWin Why do you prefer do.call/strsplit than eval/parse? I'm aware that some people don't like to use eval/parse. Is there any reason? Thanks.
I'm just trying to follow the of advice of the majority of my betters. Since I consider you to also be one of my betters, I admit to being conflicted.
I agree that do.call is cleaner in most cases. But here, as the arguments (i.e., R expression) are provided as a string, probably parse is cleaner. So it think it's on a case by case basis.
|
3

Here's a third answer that both uses parse, alist and do.call. My motivation for this new answer, is in the case where arguments are passed interactively from a client-side as chars. Then I guess, there is no good way around not using parse. Suggested solution with strsplit, cannot understand the context whether a comma , means next argument or next argument within an argument. strsplit does not understand context as strsplit is not a parser.

here arguments can be passed as "a=c(2,4), b=3,5" or list("c(a=(2,4)","b=3","5")

#' convert and evaluate a list of char args to a list of arguments
#'
#' @param listOfCharArgs a list of chars 
#'
#' @return
#' @export
#'
#' @examples
#' myCharArgs = list('x=c(1:3,NA)',"trim=0","TRUE")
#' myArgs = callMeMaybe(myCharArgs)
#' do.call(mean,myArgs)
callMeMaybe2 = function(listOfCharArgs) {
  CharArgs = unlist(listOfCharArgs)
  if(is.null(CharArgs)) return(alist())
    .out = eval(parse(text = paste0("alist(",
      paste(parse(text=CharArgs),collapse = ","),")")))
}

myCharArgs = list('x=c(1:3,NA)',"trim=0","TRUE")
myArgs = callMeMaybe2(myCharArgs)
do.call(mean,myArgs)
 [1] 2

1 Comment

my first answer was fairly unsafe, as a user could have e.g. "quit('no')" or what not evaluated
1

Using all of do.call, eval and parse (combining kohske's and csgillespie's answers, and also WoDoSc's answer to 'Pass a comma separated string as a list'):

x <- c(1:10)
myoptions <- "trim = 0, na.rm = FALSE"

do.call(
  what = mean,
  args = append(list(x = x), eval(parse(text = paste0("list(", myoptions, ")"))))
)

This solution can be quite resilient in a more complex case, such as shown below.

myfn <- function(x, y = 0, z = 0, ...) {
  print(paste("x:", x))
  print(paste("y:", y))
  print(paste("z:", z))
  if (length(list(...)) > 0) {
    print("other:")
    print(list(...))
  }
}

myextraargs <- paste(
  "y = c(11, 14), z = 47,",
  "t = data.frame(p = c('apple', 'plum'), j = c(7, 2), k = c(3, 21))"
)

do.call(
  what = myfn,
  args = append(
    list(x = 7),
    eval(parse(text = paste0("list(", myextraargs, ")")))
  )
)

results in:

[1] "x: 7"
[1] "y: 11" "y: 14"
[1] "z: 47"
[1] "other:"
$t
      p j  k
1 apple 7  3
2  plum 2 21

...and...

myextraargs <- NULL

do.call(
  what = myfn,
  args = append(
    list(x = 7),
    eval(parse(text = paste0("list(", myextraargs, ")")))
  )
)

results in

[1] "x: 7"
[1] "y: 0"
[1] "z: 0"

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.