Using user-selected input as function parameter in shiny? - r

Suppose I have in my shiny ui.R an input:
selectInput("choice","Select Values",choices=c('a','b','c',FALSE),
selected="FALSE")
And I want to use this input as a parameter to a function in server.R. The default value for this parameter is FALSE, but can take in character values. I want to set it so that, for example, if the user select 'a', the value 'a' would be passed on to the parameter in the function, and if the user select 'FALSE', the default value of FALSE is passed on to the parameter.
The way I tried to do this is by:
choice <- ifelse(input$choice=='FALSE',FALSE,input$choice)
and then use it in the function:
sample_func(param=choice)
However, this gives me an error "param has the wrong format". The error is the result of choice not being a character value or FALSE.
What might be the reason for this?

The first problem is:
choice <- ifelse(input$choice=='FALSE',FALSE,input$choice)
This must be written in a reactive context as it depends on user input. So you have to wrap your ifelse(...) part in reactive(). Of course I don't know if you had done so already since you didn't post the entire code and I assume you just wrote that code without the wrapping of reactive().
Another thing concerning this same line is not really a bug, but a habit. It's a bad idea to use choice again. The point is, it already existed as in input$choice and now you assign it to a reactive value of the same name. That could be confusing if you have a complex and long code.
So, I would suggest something like this below.
fun_choice <- reactive(ifelse(input$choice=='FALSE',FALSE,input$choice))
The next problem is in:
sample_func(param=fun_choice) ## I changed the argument name to fun_choice
If you had treated fun_choice as reactive, That should be replaced by fun_choice(). Also, that would mean that this function result should also be in reactive context. So the line should become:
variable_name <- reactive( sample_func(param=fun_choice()) )
Of course, it could be wrapped in observe if what you want is the side effect and some other things but not the function return.
Hope this help.
One last thing, if fun_choice is of no other use elsewhere but just passing into sample_func, I would suggest you put them in one reactive(). i.e.
variable_name <- reactive({
fun_choice <- ifelse(input$choice == 'FALSE', FALSE, input$choice)
sample_func(param = fun_choice)
})
Note that in this method, fun_choice, within the same reactive(), need not be expressed as fun_choice(), but simply fun_choice.

Related

code executes outside function, but not inside it

my problem is, that some code gets executed outside a function, but not in it. In my example, the content of certain cells should be transferred from the input table to the output table. In case of removal or adding of rows/cols I don't access the cells by their index (e.g input[3,4]), but by application of a condition (e.g. input[(which(input$code=="A1")),(which(colnames(input)=="kg"))].
so here's a minimized version of my data:
input<-data.frame(animal=c("cat","dog","mouse","deer","lion"),
m=c(0.5,1,0.1,1.5,3),
kg=c(5,20,0.2,50,100),
code=c("A4","A5","A3","A1","A2"))
output<-data.frame(code=c("A1","A2","A3","A4","A5"),
kg=numeric(5))
execution outside the function, that works (the content of a cell of the input table should be copied to a suitable one in the output table):
row_out<-which(output$code=="A1")
col_out<-which(colnames(output)=="kg")
row_in<-which(input$code=="A1")
col_in<-which(colnames(input)=="kg")
output[row_out,col_out]<-input[row_in,col_in]
and the function, that contains the same code, which worked outside, except for the substitution of the quoted code expression for a function argument (codeexpression):
fun_transfer<-function(codeexpression){
row_out<-which(output$code==codeexpression)
col_out<-which(colnames(output)=="kg")
row_in<-which(input$code==codeexpression)
col_in<-which(colnames(input)=="kg")
output[row_out,col_out]<-input[row_in,col_in]
}
Problem: now the execution of
fun_transfer("A4")
does not lead to an error, nor to a result in the output table.
Why doesn't this function work or rather what does it do? Is there a problem with quotation marks?
any help would be appreciated
thanks,
Michel
In the best case, data enters a function as argument and leaves it as a return value.
Outside of a function
output[row_out,col_out] <- input[row_in,col_in]
changes the existing data.frame. You can (or better: should) not change some variable outside the function from within the function.
Just end your function with a return statement to return the changed dataframe to the caller
Edit
It appears as if what you try to write is a lesser version of merge. If the following answers your question it will probably be more concise, faster and more idiomatic:
input<-data.frame(animal=c("cat","dog","mouse","deer","lion"),
m=c(0.5,1,0.1,1.5,3),
kg=c(5,20,0.2,50,100),
code=c("A4","A5","A3","A1","A2"))
output<-data.frame(code=c("A1","A2","A3","A4","A5"))
output <- merge(output, input[, c("code", "kg")], by = "code",
all.x = TRUE, all.y = FALSE)
print(output)

Preserve a promise in R

I want to, essentially, pass a value untouched through a function. So in the following example (in Rstudio):
example_function <- function(datain){
as.environment("package:utils")$View(datain)
}
I want the inner function to act as if I'm passing it the original object, in particular so the name which appears in the View window will have the name of the original object (X, say) rather than datain which is what currently occurs.
With deparse(substitute(datain)) you can get the original name of the argument passed.
Then, to accomplish what you asked for, you can simply do
example_function <- function(datain){
as.environment("package:utils")$View(datain, deparse(substitute(datain)))
}
Now the View window will be titled appropriately as you wanted.
However note that "I want the inner function to act as if I'm passing it the original object" request of yours is not possible in R. R does not support pass-by-reference. There are some workarounds, but if you only needed if for naming the View, the above fix should be fine.
You can also use get for this.
example_function <- function(datain){
as.environment("package:utils")$View(get(datain),datain)
}
in this case you don't pass the variable but rather the name of the variable as a string.
example_function("X")

R: IF statement evaluating expression despite condition being FALSE?

I've got a large function in R and the users have the ability to not include/specify an object. If they DO, the code checks to make sure the names in that object match the names in another. If they DON'T, there's no need to do that checking. The code line is:
if(exists("grids")) if(!all(expvarnames %in% names(grids))) {stop("Not all expvar column names found as column names in grids")}
But I'm getting the following error:
Error in match(x, table, nomatch = 0L) : argument "grids" is missing, with no default
Well in this trial run, grids is SUPPOSED to be missing. If I try
if(exists("grids")) print("yay")
Then nothing prints, i.e. the absence of grids means the expression isn't evaluated, which is as I'd expect. So can anyone think why R seems to be evaluating the subsequent IF statement in the main example? Should I slap another set of curly brackets around the second one??
Thanks!
Edit: more problems. Removing "grids," from the functions list of variables means it works if there's no object called grids and you don't specify it in the call (i.e. function(x,grids=whatever)). And keeping "grids," IN the functions list of variables means it works if there IS an object called grids and you do specify it in the call.
Please see this: http://i.imgur.com/9mr1Lwi.png
using exists(grids) is out because exists wants "quotes" and without em everything fails. WITH them ("grids"), I need to decide whether to keep "grids," in the functions list. If I don't, but I specify it in the call (function(x,grids=whatever)) then I get unused argument fail. If I DO, but don't specify it in the call because grids doesn't exist and I don't want to use it, I get match error, grids missing no default.
How do I get around this? Maybe list it in the function variables list as grids="NULL", then rather than if(exists("grids")) do if(grids!="NULL")
I still don't know why the original match problem is happening though. Match is from the expvarnames/grids names checker, which is AFTER if(exists("grids")) which evaluates to FALSE. WAaaaaaaiiiiittttt..... If I specify grids in the function variables list, i.e. simply putting function(x,grids,etc){do stuff}, does that mean the function CREATES an object called grids, within its environment?
Man this is so f'd up....
testfun <- function(x,grids)
{if(exists("grids")) globalgrids<<-grids
print(x+1)}
testfun(1) # Error in testfun(1) : argument "grids" is missing, with no default
testfun <- function(x,grids)
{if(exists("grids")) a<<-c(1,2,3)
print(x+1)}
testfun(1) #2 (and globally assigns a)
So in the first example, the function seems to have created an object called "grids" because exists("grids") evaluates to true. But THEN, ON THE SAME LINE, when asked to do something with grids, it says it doesn't exist! Schroedinger's object?!
This is proven in example 2: grids evaluates true and a is globally assigned then the function does its thing. Madness. Complete madness. Does anyone know WHY this ridiculousness is going on? And is the best solution to use my grids="NULL" default in the functions variables list?
Thanks.
Reproducible example, if you want to but I've already done it for every permutation:
testfun <- function(x,grids)
{if(exists("grids")) if(!all(expvarnames %in% names(grids))) {stop("Not all expvar column names found as column names in grids")}
print(x+1)}
testfun(1)
testfun(x=1,grids=grids)
grids<-data.frame(c(1,2,3),c(1,2,3),c(1,2,3))
expvarnames <- c("a","b","c")
colnames(grids) <- c("a","b","c")
Solution
Adapting your example use:
testfun <- function(x,grids = NULL)
{
if(!is.null(grids)){
if(!all(expvarnames %in% names(grids))){
stop("Not all expvar column names found as column names in grids")
}
print(x+1)
}
}
Using this testfun(1) will return nothing. By specifying a default argument in the function as NULL the function then checks for this (i.e. no argument specified) and then doesn't continue the function if so.
The Reason the Problem Occurs
We go through each of the examples:
testfun <- function(x,grids)
{if(exists("grids")) globalgrids<<-grids
print(x+1)}
testfun(1) # Error in testfun(1) : argument "grids" is missing, with no default
Here we call the function testfun, giving only the x argument. testfun knows it needs two arguments, and so creates local variables x and grids. We have then given an argument to x and so it assigns the value to x. There is no argument to grids, however the variable has still been created, even though no value has been assigned to it. So grids exists, but has no value.
From this exists("grids") will be TRUE, but when we try to do globalgrids<<-grids we will get an error as grids has not been assigned a value, and so we can't assign anything to globalgrids.
testfun <- function(x,grids)
{if(exists("grids")) a<<-c(1,2,3)
print(x+1)}
testfun(1) #2 (and globally assigns a)
This, however is fine. grids exists as in the previous case, and we never actually try and access the value stored in grids, which would cause an error as we have not assigned one.
In the solution, we simply set a default value for grids, which means we can always get something whenever we try and access the variable. Unlike in the previous cases, we will get NULL, not that nothing is stored there.
The main point of this is that when you declare arguments in your function, they are created each time you use the function. They exist. However, if you don't assign them values in your function call then they will exist, but have no value. Then when you try and use them, their lack of values will throw an error.
> a <- c(1,2,3,4)
> b <- c(2,4,6,8)
> if(exists("a")) if(!all(a %in% b)) {stop("Not all a in b")}
Error: Not all a in b
> rm(a)
> if(exists("a")) if(!all(a %in% b)) {stop("Not all a in b")}
>
When a does not exist, the expression does not evaluate, as expected. Before testing your first expression, make sure that grids does not exist by running rm(grids) in the console.
Richard Scriven's comment got me thinking: grids was an argument in my function but was optional, so maybe shouldn't be specified (like anything in "..." optional functions). I commented it out and it worked. Hooray, cheers everyone.

Passing arguments to functions, and variable scopes in R

I'm writing a simple function that takes two arguments (state, outcome). State is used to subset a dataframe later.
Having said that, part of the requirement is that state be a length 2 character vector. I need to write more code to ensure that the state that is passed conforms to this requirement.
So I wrote the following:
best <- function(state, outcome) {
outcome <- read.csv("outcome-of-care-measures.csv", colClasses = "character")
state <- vector(mode = "character", length = 2)
st.checkTbl <- outcome[8]
state
}
However, when I call the function and pass the arguments:
best("AXA") or best("FOO") or even best("TX") or best(AL)
All I get back is: "" ""
If I comment out the #state <- ... then it passes the argument just fine and it prints "FOO" or "AXA" or "TX", etc.
How can I ensure that the argument passed to the function is stored as a variable (state) in the function? Or, am I way overthinking this? Really I just wanted to test that what I am passing to the state argument can be printed for test purposes.
. Sorry for the 101 lesson.
You would generally read your data outside of any function, like so:
outcome.data <- read.csv("outcome-of-care-measures.csv", colClasses = "character")
Otherwise, since a function has its own namespace, all the variables defined inside of it will vanish upon its return, unless they themselves are returned by the function with return(...). Several objects can be returned by putting them in a list: return(list(item1=var1, item2=var2)).
Some functions, such as assign, have the envir parameter that can be set to .GlobalEnv to change this behavior. Altering an object can also be done inside a function using the <<- operator instead of <-, although this practice is generally recommended against.
As a side note, when using a function, you need to define clearly:
What are its inputs
What does it do
What does it return
It's not useful, for instance, to use outcome as a function parameter and then read into a variable named income the content of a csv file. Your argument is then useless as it will be written over. That's why you had to comment out the line defining your state variable inside the function to actually be able to use state as it was received by the function.
This surely won't answer all your questions, but hopefully it can help you clarify certain things. For the rest there are plenty of good tutorials to learn further on how to program in R and how/when to use functions. Best of luck and happy learning!

R Finding name of assignee variable from inside called function

Say that I have the following function:
myfun <- function(x){
assignee <- get_assignee_name()
message(paste0("output assigned to ",assignee,"!"))
x
}
my_var <- myfun(7)
output assigned to my_var!
I am looking for something that does the job of get_assignee_name() above.
It should be a function or a few lines of code that is called from inside a function, it examines the call that it occurs inside of and returns the object that is being assigned to as a character, if one exists. So in the example
answer <- myfun(10)
the message "output assigned to answer!" should appear.
I am aware that this seems unnecessarily complicated, but it is just an example to demonstrate the point for a more complicated use case. I am also aware that of ways around this involving passing the variable name into the function as a parameter and then calling assign. This won't solve the problem in this case, where this should be transparent to the user
This is not possible because <- is a primitive and quite special function. It is not part of the call stack. You would need to use assign instead:
myfun <- function(x){
assignee <- sys.call(1)
if (assignee[[1]] == quote(assign))
message(paste0("output assigned to ",assignee[[2]],"!"))
x
}
assign("my_var", myfun(7))
#output assigned to my_var!
my_var
#[1] 7
However, I suggest you reconsider how you approach your actual issue.

Resources