2015-08-23 1 views
1

J'ai une fonction commeComment passer ensemble de paramètres inconnus à la fonction R

myfunc <- function(x, y=x){ 
    x+y 
} 

qui par défaut (évidemment) la valeur de y à x si y est pas passé à la fonction.

Maintenant, j'utilise optparse pour lire certains arguments de ligne de commande à un script qui appelle myfunc.

# myscript.R 

option_list <- list(
    make_option(c("--x"), type="numeric"), 
    make_option(c("--y"), type="numeric") 
) 

print(myfunc(opt$x, opt$y)) 

Le problème avec le code ci-dessus est, il oblige l'utilisateur à fournir une valeur pour y (sinon une erreur sera levée). Au lieu de cela, je voudrais appeler myfunc en utilisant tous et seulement les paramètres fournis par l'utilisateur. Comment puis-je accomplir cela de la manière la plus élégante, la plus évolutive, la plus généralisable? Remarque - pour ceux qui connaissent Python, je pense que je veux faire quelque chose de similaire au déballage de dictionnaire en utilisant les valeurs opt.

+0

Cela dépend en quelque sorte de ce que vous allez faire avec ces paramètres. Pour une sommation simple, '...' suffirait, où vous collecterez les arguments passés à '...' et les écraserez. –

Répondre

3

Si vous avez une liste nommée d'arguments de optparse que vous souhaitez fournir une fonction, vous pouvez le faire avec do.call:

# Provide x and y 
opts <- list(x=2, y=3) 
do.call(myfunc, opts) 
# [1] 5 

# Provide only x 
opts <- list(x=2) 
do.call(myfunc, opts) 
# [1] 4 

Comme vous le soulignez, cela est essentiellement "dictionary unpacking"/"splatting" de Python. On peut ensuite utiliser optparse pour saisir les valeurs de x et y, ce qui y une entrée en option sur la ligne de commande:

# optparse.R 
library(optparse) 
option_list <- list(make_option("--x", type="integer", default=0), 
        make_option("--y", type="integer")) 
opt <- parse_args(OptionParser(option_list=option_list)) 
myfunc <- function(x, y=x) x+y 
do.call(myfunc, opt[names(opt) %in% c("x", "y")]) 
# > Rscript optparse.R --x=2 --y=3 
# [1] 5 
# > Rscript optparse.R --x=2 
# [1] 4 

En utilisant la fonction de R étant appelé pour calculer la valeur par défaut de y au lieu de la commande -l'analyseur, il est simple de faire des réglages par défaut plus compliqués pour y, comme y=min(0, x).

# optparse.R 
library(optparse) 
option_list <- list(make_option("--x", type="integer", default=0), 
        make_option("--y", type="integer")) 
opt <- parse_args(OptionParser(option_list=option_list)) 
myfunc <- function(x, y=min(0, x)) x+y 
do.call(myfunc, opt[names(opt) %in% c("x", "y")]) 
# > Rscript optparse.R --x=-2 
# [1] -4 
# > Rscript optparse.R --x=2 
# [1] 2 
2

Je préfère le plus récent docopt sur optparse que j'ai trouvé docopt être

  • plus facile à utiliser (voir ci-dessous)
  • largement disponible pour de nombreux langauages, voir docopt.org

Voici un exemple minimal qui fournit également les valeurs par défaut qui est votre con cern ici. Tout d'abord le code:

#!/usr/bin/Rscript 

library(methods) # as Rscript does not load it 
library(docopt) 

## simple helper function 
myfunc <- function(x, y) as.numeric(x) + as.numeric(y) 

doc <- "Usage: myscript [-x x] [-y y] 
-x x Numeric value for x [default: 0.0] 
-y y Numeric value for y [default: -7.89]" 

opt <- docopt(doc) 

print(myfunc(opt$x, opt$y)) 

Ensuite, l'utilisation:

[email protected]:/tmp$ ./myscript.R --help 
Usage: myscript [-x x] [-y y] 
-x x Numeric value for x [default: 0.0] 
-y y Numeric value for y [default: -7.89] 
[email protected]:/tmp$ ./myscript.R 
[1] -7.89 
[email protected]:/tmp$ ./myscript.R -y 40 -x 2 
[1] 42 
[email protected]:/tmp$ 

Notez que nous devons utiliser as.numeric() sur les valeurs que la seule chose qui docopt ne le fait pas est de garantir/enforce un type.

+0

Merci pour la réponse, Dirk. Je vois que vos valeurs par défaut sont les deux constantes, mais que se passe-t-il si je veux que la valeur par défaut de y soit quelque chose comme min (0, x)? – Ben

+0

Je ne pense pas que vous puissiez avoir des interdépendances _dans l'argument passé à 'docopt()' _ mais vous devriez pouvoir le réconcilier _afterwards_ une fois que vous avez obtenu l'objet résultant.Ce qui est exactement ce que l'autre réponse fait aussi ... –