R optparse and short flag arguments - r

I can't get optparse to work with short arguments. What's wrong with this?
library("optparse")
packageVersion("optparse")
option_list = list(
make_option(c("-f", "--file"), action = "store", type="character", default=NULL)
)
opt_parser = OptionParser(option_list=option_list)
parse_args(opt_parser, args=c("--file=anyfile.txt")) # this works
parse_args(opt_parser, args=c("-f anyfile.txt")) # but this does not
[1] ‘1.7.1’
Error in getopt_options(object, args) :
Error in getopt(spec = spec, opt = args) :
short flag "f" requires an argument, but has none

args is a character vector. From help:
A character vector containing command line options to be parsed. Default is everything after the Rscript program in the command line. If positional_arguments is not FALSE then parse_args will look for positional arguments at the end of this vector.
args=c("-f", "anyfile.txt")

Related

How to call R script from command line with multiple augument types (inc. list)

I have been working on this for a while and I am still stuck.
I would like to call an Rscript using multiple arguments from what is essentially a command line (Snakemake file). The main difference between what I am asking and what I see on SO (How to pass list of arguments to method in R?, How can I pass an array as argument to an R script command line run?, Is it possible to pass an entire list as a command line argument in R) is that my arguments are a combination of strings, numbers, and a list.
Here is the set up in my rules (Snakemake file):
rule cluster_plots_DGE:
input:
script = 'src/scripts/create_images_DGE.R',
analyze_sc_object_output = sc_objects
params:
project = PROJECT,
method = METHOD,
rpath = RPATH,
storage=STORAGE,
components = COMPONENTS,
reso_file = resolution_file,
sample_files = integrated_seurat_objects
output: dge_files
log:
log_output = log_directory + PROJECT.lower() + '_DGE.log'
shell:
"Rscript {input.script} {params.project} {params.method} {params.rpath} {params.storage} {params.components} {params.reso_file} {params.sample_files} 2> {log.log_output}"
Here is what the call translates to:
Rscript src/scripts/create_images_DGE.R project_name ALL path_to_R_installed_libraries rds 50 data/endpoints/project_name/analysis/PCA_14/tables/project_nameR_resolution_list.txt data/endpoints/project_name/analysis/PCA_14/RDS/project_name_Standard_0.5.RDS data/endpoints/project_name/analysis/PCA_14/RDS/project_name_RPCA_0.5.RDS data/endpoints/project_name/analysis/PCA_14/RDS/project_name_SCT_0.5.RDS 2> logs/DGE_Markers/project_name_DGE.log
Where sample_files = integrated_seurat_objects is a list containing:
data/endpoints/project_name/analysis/PCA_14/RDS/project_name_Standard_0.5.RDS,
data/endpoints/project_name/analysis/PCA_14/RDS/project_name_RPCA_0.5.RDS,
data/endpoints/project_name/analysis/PCA_14/RDS/project_name_SCT_0.5.RDS
And here is the beginning of my R script:
args = commandArgs(trailingOnly=TRUE)
compo <- ''
project <- ''
method <- ''
lib_path <- ''
storage <- ''
res_file <- ''
integrated_object <- '' #list of objects
# test if there is at least 7 arguments: if not, return an error
if (length(args) < 7) {
stop('At least seven arguments must be supplied.', call.=FALSE)
} else if (length(args)==7) {
project = args[1]
method = args[2]
lib_path = args[3]
storage = args[4]
compo = args[5]
res_file = args[6]
integrated_object = args[7]
#integrated_object = eval(parse(text=args[7]))
}
print(compo)
print(project)
print(method)
print(lib_path)
print(storage)
print(res_file)
print(integrated_object)
If I use the entire integrated_seurat_objects list, this is what gets returned:
[1] ""
[1] ""
[1] ""
[1] ""
[1] ""
[1] ""
[1] ""
If I take the first entry from integrated_seurat_objects and pass that as an argument, I get (I have replaced the actual project name and paths is this post):
[1] "50"
[1] project_name
[1] "ALL"
[1] library_path_to_R_libraries
[1] "rds"
[1] "data/endpoints/project_name/analysis/PCA_14/tables/project_name_resolution_list.txt"
[1] "data/endpoints/project_name/analysis/PCA_14/RDS/project_name_Standard_0.5.RDS"
It seems do-able but I have not cracked it yet. How can I pass multiple arguments that include a list to an R script form the command line? Any assistance is always appreciated.
#MrFlick deserves credit for this answer. The issue was I was not accounting for a situation where the number of arguments would be greater than 7 (duh).
A quick very fix:
if (length(args) < 7)
{
stop('At least seven arguments must be supplied.', call.=FALSE)
}
if (length(args)==7)
{
project = args[1]
method = args[2]
lib_path = args[3]
storage = args[4]
compo = args[5]
res_file = args[6]
integrated_object = args[7]
}
if (length(args)>7)
{
project = args[1]
method = args[2]
lib_path = args[3]
storage = args[4]
compo = args[5]
res_file = args[6]
integrated_object = args[7:length(args)]
}
Thank you for your eyes #MrFlick

R: Parsing boolean command line argument in using argparse

I am using "argparse" library in R for command line arguments.
# Create parser
parser = ArgumentParser(description='command line args')
# Add command line arguments
parser$add_argument("is_local", nargs='?', type="logical",
help="whether to use local or server path", default=FALSE)
parser$add_argument("alert", nargs='?', type="double",
help="alert threshold", default=0.99)
I am trying to call it on command line such as:
Rscript my_func.R TRUE 0.99
However boolean argument does not change. Any idea how to parse boolean argument in R?
Thanks!
I don't know R, but the description of this package says it's a wrapper for the Python argparse.
I would recommend changing these:
parser$add_argument("is_local", nargs='?', type="logical",
help="whether to use local or server path", default=FALSE)
parser$add_argument("alert", nargs='?', type="double",
help="alert threshold", default=0.99)
to
parser$add_argument("--local", action='store_true'),
help="whether to use local or server path")
parser$add_argument("--alert", type="double",
help="alert threshold", default=0.99)
which would be called with
Rscript my_func.R --local --alert 0.99
store_true is illustrated on the basic docs page, https://github.com/trevorld/r-argparse
If I read the R correctly, your is_local should be giving you a warning
"You almost certainly want to use action='store_true' or action='store_false' instead"
A store_true argument sets the attribute to TRUE if present, and the default FALSE if absent. It should be an optional (--) and not set the nargs.
(It is possible to have an argument that takes strings 'true' and 'false' (or any other pair in your native language) and converts them to logical values, but requires more coding.)
I made --alert a flagged argument as well, without the nargs. Its value will be the default if absent, and the convert the string to a double if provided. It could be a '?' positional, but while learning I think it's best to stick with optionals unless you want the argument to be required.
The R-argparse docs aren't very complete. You may need to refer to the Python docs, and experiment to get the translation right.
https://docs.python.org/3/library/argparse.html
Thanks for your time and help!
I would like to share my workaround for the problem as I find original argparse (action) usage a bit complicated.
# Convert str input to boolean
str2bool = function(input_str)
{
if(input_str == "0")
{
input_str = FALSE
}
else if(input_str == "1")
{
input_str = TRUE
}
return(input_str)
}
# Create parser
parser = ArgumentParser(description='command line args')
# Add command line arguments
parser$add_argument("is_local", nargs='?', type="character",
help="whether to use local or server path", default="1")
parser$add_argument("alert", nargs='?', type="double",
help="alert threshold", default=0.99)
# Parse arguments
args = parser$parse_args()
# Convert str arguments
args$is_local = str2bool(args$is_local)
# Call on CMD line
Rscript my_func.R 1 0.99 #equivalent Rscript my_func.R TRUE 0.99
Rscript my_func.R 0 0.99 #equivalent Rscript my_func.R FALSE 0.99
Below is the sample example given in the official docs of package argparse:
parser <- ArgumentParser(description='Process some integers')
parser$add_argument('integers', metavar='N', type="integer", nargs='+',
help='an integer for the accumulator')
parser$add_argument('--sum', dest='accumulate', action='store_const',
const='sum', default='max',
help='sum the integers (default: find the max)')
parser$print_help()
# default args for ArgumentParser()$parse_args are commandArgs(TRUE)
# which is what you'd want for an Rscript but not for interactive use
args <- parser$parse_args(c("--sum", "1", "2", "3"))
accumulate_fn <- get(args$accumulate)
print(accumulate_fn(args$integers))
Here is the link for the argparse pdf https://cran.r-project.org/web/packages/argparse/argparse.pdf
I hope it might help.

Why does formals function return NULL on functions defined with arguments?

From base R, formals function should grant access and allow manipulation of the Formal Arguments.
So, why does it return NULL on some functions defined with classic arguments ?
> ls
function (name, pos = -1L, envir = as.environment(pos), all.names = FALSE,
pattern, sorted = TRUE)
{ ...
> formals(ls)
$name
$pos
-1L
$envir
as.environment(pos)
$all.names
[1] FALSE
$pattern
$sorted
[1] TRUE
> sum
function (..., na.rm = FALSE) .Primitive("sum")
> formals(sum)
NULL
I expect formals(sum) to provide a length two list result, not NULL
The problem with your choice of function is apparent from its body. Primitive functions are somewhat different in that their argument list may have some names but their arguments are passed to C code and formals(), body(), and environment() all return NULL. Try it with apply instead.

R script reporting wrong command line arguments

Apparently, in R, one can get args with the commandArgs() function.
Let's try. The file's name is wtf.r:
#!/usr/bin/Rscript
args = commandArgs()
asdf = list()
asdf$arg1 = args[2] # Starting from 2 bc IIUC the first element is supposed to be the program's name
asdf$arg2 = args[3]
asdf$arg3 = args[4]
print(asdf)
Let's try running it:
my#comp:~/wtfdir$ ./wtf.r arg1 arg2 arg3
$arg1
[1] "--slave"
$arg2
[1] "--no-restore"
$arg3
[1] "--file=./wtf.r"
Wha..aat?!
Why is arg1 set to --slave? Why is arg2 set t0 --no-restore? Why is arg3 set to --file=./wtf.r?! I don't remember entering anything of the sort in the command line!
What is going wrong in here?

Unary plus for S4 class in R

I am experimenting with S4 classes in R and I was trying to define a plus (+) operator for my objects, i.e. overload the plus operator. I managed to overload the binary +, but I cannot figure out how to overload the unary plus. Here is a minimal working (unary operator not working) example for what I am trying to achieve:
setClass("HWtest",
representation(expr = "character"),
prototype = list(expr = NA_character_)
)
H <- new("HWtest", expr="Hello")
W <- new("HWtest", expr="World")
setMethod("+", signature(e1="HWtest", e2="HWtest"),
function(e1,e2){
new("HWtest",
expr = paste(e1#expr," + ",e2#expr))
}
)
Now I can use the + operator and it works smoothly:
H+W
An object of class "HWtest"
Slot "expr":
[1] "Hello + World"
Now the unary plus of course doesn't work, so that has to be overloaded
+H
Error in +H : invalid argument to unary operator
So I tried to overload it in the following way:
setMethod("+", signature(e="HWtest"),
function(e){
new("HWtest",
expr = paste("+ ",e#expr))
}
)
But this generates the error:
Error in match.call(fun, fcall) :
argument 1 matches multiple formal arguments
Is it possible to overload the unary plus? If so, how would I do it for this minimal example?
Try adding this (in addition to your binary overload):
setMethod("+", signature(e1 = "HWtest", e2 = "missing"),
function(e1){
new("HWtest",
expr = paste("+ ", e1#expr))
}
)
R> +H
An object of class "HWtest"
Slot "expr":
[1] "+ Hello"
R> H + W
An object of class "HWtest"
Slot "expr":
[1] "Hello + World"
Citing the help file ?groupGeneric,
[...] when a unary operator is encountered the Ops method is called
with one argument and e2 is missing.
And as Frank points out, the ?setMethod help file contains some useful information regarding the use of missing as a method signature (emphasis added):
Two additional special class names can appear: "ANY", meaning that
this argument can have any class at all; and "missing", meaning that
this argument must not appear in the call in order to match this
signature.

Resources