renderText in RShiny with conditions If Else - r

I am in my server.r file and trying to create an output via renderText with a conditional statement. The below code is throwing me the error:
Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)```
I have a feeling I have the architecture of this code wrong.
if (A > B)
{
output$sample <- renderText({ do some calculation)})
}
else if(A <= B)
{
output$sample <- renderText({do some other calculation)})
}
I have attempted to reformat to the below but get the same error. I feel I may be fundamentally wrong in my approach here. Any help welcomed.
output$sample <-
if (A > B)
{
renderText({ do some calculation)})
}
else if(A <= B)
{
renderText({do some other calculation)})
}

Server part where the issue was has been resolved here with some reactive objects. Please try this
ab <- reactive({req(input$account_value) - req(input$blocked_funds)})
# free funds
output$free_funds <- renderText({ab()})
# current margin
cm <- reactive({
req(input$account_value,input$blocked_funds)
if (input$account_value > input$blocked_funds){
curmargin <- round(input$account_value/(input$account_value+input$blocked_funds), digits = 2)
}else {
curmargin <- round((.5*(input$account_value))/input$blocked_funds, digits = 2)
}
})
output$current_margin <- renderText({cm()})
rm <- reactive({
req(input$account_value,input$blocked_funds)
round(input$account_value/(input$account_value + input$blocked_funds*2.5)*100,digits = 1)
})
# New margin
output$revised_margin <- renderText({
paste(rm(),"%",sep = "")
})

Related

R Shiny: test existence of reactive value which is optional after req()

I have code like this
myfx <- reactive({
req(
isTruthy(input$value),
isTruthy(data1()) || isTruthy(data2())
)
...
if(exists(data2())) {
# do some stuff
}
## do this other stuff regardless
})
The if(exists()) piece is where I am stuck. Exists is not the right function here, nor does validate(need(data2())) work. How can I conditionally execute some code if one of the optional reactives (from a group where at least one is required) exists?
EDIT 1: To make explicit the problem, see the issue illustrated by the debug prints below:
myfx <- reactive({
req(
isTruthy(input$value),
isTruthy(data1()) || isTruthy(data2())
)
print("I am printed, and data2() has not been uploaded by user")
print(isTruthy(data2()))
print("I am never printed")
if(isTruthy(data2())) {
# do some stuff
}
## do this other stuff regardless
})
Edit 2: ok I see the reason. And I also see that I'm not observing the behavior in my first req() call because of short-circuiting (||). But now I'm stuck how to achieve the behavior I want. Bascially I don't calculate data2() until the user uploads something (use a req() here too). So that is where this is hanging. But if I remove the req() from the top of this, then I get errors due to trying to work on input that doesn't exist. See the definition for data2() below. How can I fix this?
data2 <- reactive({
req(input$data2)
read.csv(input$data2$datapath) %>%
as_tibble() %>%
return()
})
You can re-use isTruthy. Since it still returns true for 0 rows, you may want to add a check for non-zero row count:
myfx <- reactive({
req(
isTruthy(input$value),
isTruthy(data1()) || isTruthy(data2())
)
...
if (isTruthy(data2()) && nrow(data2()) > 0) {
# do some stuff
}
## do this other stuff regardless
})
Or you can capture the attempt in a try/tryCatch and react accordingly:
myfx <- reactive({
req(
isTruthy(input$value),
isTruthy(data1()) || isTruthy(data2())
)
...
res2 <- tryCatch({
# do some stuff with data2()
}, error = function(e) NULL)
## do this other stuff regardless
})
Ok, here is the final working solution.
myfx <- reactive({
req(
isTruthy(input$value),
isTruthy(data1()) || isTruthy(data2())
)
print("I am printed, and data2() has not been uploaded by user")
print(isTruthy(data2()))
print("I am never printed")
if(isTruthy(data2())) {
# do some stuff
}
## do this other stuff regardless
})
data2 <- reactive({
if(isTruthy(input$data2)) {
read.csv(input$data2$datapath) %>%
as_tibble() %>%
return()
}
})
raise an error if dat2 doesn't exit by checking number of rows
chk will become NULL if dat2 doens't exist
use chk in if statement to determine what to do
chk <- tryCatch({ nrow(dat2()) > 0}, error = function(e) NULL)
if(is.null(chk)){
dat1
} else{
cbind(dat1, dat2)
}

How to access secondary inputs with variable names in R Shiny?

I'm trying to have the user enter a numeric input, then generate a number of input boxes equal to that first numeric input. I would then like to find the sum of the responses to these secondary numeric inputs. However, I am having trouble accessing these variables in a comprehensive way, since their names are created used numeric variables. Right now when I run it I get this error:
Warning: Error in get: object 'inp21' not found
Thanks
library(shiny)
ui <- fluidPage(
# Application title
titlePanel("Test"),
sidebarLayout(
sidebarPanel(
numericInput("inp1",
"Enter num:",1)
),
mainPanel(
uiOutput("more_inp"),
textOutput("num_inps")
)
)
)
server <- function(input, output) {
counter <- reactiveValues(countervalue = 0)
counter2 <- reactiveValues(counter2value = 0)
output$more_inp <- renderUI({
#Generates number of numeric inputs equal to original numeric input
mylist <- lapply(1:input$inp1, function(i) {
inp_identifier <- paste("inp2", i, sep="")
inp_name<- paste("Input2",i,sep=" ")
list(
numericInput(inp_identifier,inp_name, 5)
)
})
do.call(tagList, unlist(mylist, recursive = FALSE))
})
#Display number of secondary inputs
#Count number of secondary inputs
observeEvent(input$inp1, {
counter$countervalue <- counter$countervalue + 1
})
#Find sum of secondary inputs
output$num_inps<-renderText(input$inp1)
observeEvent(input$inp1,{
for (i in 1:counter$countervalue) {
counter2$counter2value <- counter2$counter2value + get(paste("inp2", i, sep=""))
print(counter2$counter2value)
}
})
}
Run the application
shinyApp(ui = ui, server = server)
Additionaly to r2evans suggestion simply "filter" out the case when an input field cannot be found. It seems that this observeEvent is called before the other input fields can be created. As a consequence, you receive an empty vector (numeric(0)) when you try to access one of them.
observeEvent(input$inp1,{
for (i in 1:counter$countervalue) {
if (isTruthy(input[[paste0("inp2", i)]])) {
counter2$counter2value <- counter2$counter2value + input[[paste0("inp2", i)]]
print(counter2$counter2value)
}
}
})
You aren't checking for the presence before adding. The observeEvent block is firing aggressively, so even though counter$countervalue is 1, there are not additional input fields present, so input[[paste("inp2", i, sep="")]] returns NULL. Anything plus null is numeric(0).
How to find this
observeEvent(input$inp1,{
browser()
for (i in 1:counter$countervalue) {
counter2$counter2value <- counter2$counter2value + get(paste("inp2", i, sep=""))
print(counter2$counter2value)
}
})
Run your app. When it hits the debugger and shows you Browse[2]>, then
counter$countervalue
# [1] 1
counter2$counter2value
# [1] 0
i <- 1L
get(paste("inp2", i, sep=""))
# Error in get(paste("inp2", i, sep = "")) : object 'inp21' not found
input[[ paste("inp2", i, sep="") ]]
# NULL
names(input)
# [1] "inp1"
A quick check could be to look for that paste(.) name in names(input).
Tangent
for (i in 1:n) works fine as long as you are 100% certain that n here will always be 1 or greater. If there is the remote possibility that it will be <1, then the results will be rather unintuitive.
That is, if n is 0, then I would expect the for loop to do nothing. As a vector example,
for (nm in c('a','b')) ... # executes twice GOOD
for (nm in c()) ... # does not execute GOOD
for (i in 1:2) ... # executes twice GOOD
for (i in 1:0) ... # executes twice BAD
The reason is that 1:0 resolves to a reverse sequence, so seq(1, 0) and therefore c(1L, 0L).
A safer alternative if you expect a possible zero is seq_len:
seq_len(2)
# [1] 1 2
seq_len(0)
# integer(0)
(seq(1, length.out=n) is equivalent.)
An even safer alternative if you cannot guarantee "positive only", then
seq_len(max(0, n))
(since seq_len(-1) is an error).
Suggested solution
observeEvent(input$inp1,{
for (i in seq_len(counter$countervalue)) { # or max(0, ...)
nm <- paste("inp2", i, sep="")
if (nm %in% names(input)) {
counter2$counter2value <- counter2$counter2value + input[[nm]]
print(counter2$counter2value)
}
}
})

Error in :: NA/NaN argument in Shiny R: Comparing Strings

So I am writing an app that lets the user input some basic operations involving a matrix. At one part, they are asked to "return" the entire matrix, as they would in base R, for which they can either "call" the matrix with rows and columns [1:5,1:5] or simply put in the matrix's name (my.matrix, for instance). I am not able to get the second part working, however, as the app will crash and return the following error: Error in :: NA/NaN argument. I included the segment of code for this particular part in the server:
observeEvent( input$go6, {
sixthinput<- as.numeric(str_extract_all(input$six, "[0-9]+")[[1]])
string6 <- str_extract(input$six, "my.matrix")
sixth.list <- my.matrix[sixthinput[1]:sixthinput[2],sixthinput[3]:sixthinput[4]]
isolate({
buttonValue$go1 = FALSE
buttonValue$go2 = FALSE
buttonValue$go3 = FALSE
buttonValue$go4 = FALSE
buttonValue$go5 = FALSE
buttonValue$go6 = TRUE
buttonValue$go7 = FALSE
})
comparestring <- "my.matrix"
if (isTRUE(identical(sixth.list, my.matrix)) & buttonValue$go6) {
output$display <- renderText({
paste(c("The matrix you extracted contains:"))
})
output$displayMat <- renderTable(
sixth.list
)
output$display2 <- renderText({
print("Correct!")
})
} else if(isTRUE(identical(string6, comparestring)) & buttonValue$go6) {
output$display <- renderText({
paste(c("The matrix you extracted contains:"))
})
output$displayMat <- renderTable(
my.matrix
)
output$display2 <- renderText({
print("Correct!")
})
} else {
output$display <- renderText({
paste(c("The matrix you extracted contains:"))
})
output$displayMat <- renderTable(
sixth.list
)
output$display2 <- renderText({
print("Incorrect")
})
}
})
I am sure it has something to do with the variable "string6," "comparestring," and/or the part where I compare them in the if loop, but I am not sure where/how exactly it is going wrong, other than something is NA. Any clarification is appreciated.
The problem is in these lines:
sixthinput<- as.numeric(str_extract_all(input$six, "[0-9]+")[[1]])
string6 <- str_extract(input$six, "my.matrix")
sixth.list <- my.matrix[sixthinput[1]:sixthinput[2],sixthinput[3]:sixthinput[4]]
When the user inputs my.matrix with no subset operator ([), the value of sixthinput will be numeric(0) because there are no matching numbers. The value of sixthinput[1] will be NA, and you can’t then subset my.matrix by NA. The easiest way to avoid the error would be to check that length(sixthinput) == 4, and if not then avoid subsetting my.matrix.

R Shiny not randomizing

I am trying to write a Shiny app for a class I'm teaching that draws a random sample from a dataset and computes summary statistics. Whenever I press the reset button on the UI, a new subset should be sampled. Here is my code so far:
shinyServer(function(input, output) {
# Output for Ch. 1 Problems: Central Tendency
# Prepare data
observeEvent(input$Ch1.Prob.CT.reset, {
Ch1.Prob.CT.n <- sample(8:12, 1)
Ch1.Prob.CT.obs <- sample(1:nrow(cars), Ch1.Prob.CT.n)
})
data <- eventReactive(input$Ch1.Prob.CT.reset, {
cars[Ch1.Prob.CT.obs, 'dist', drop=F]
})
# Outputs
output$Ch1.Prob.CT.Data <- renderDataTable({
data()
})
output$Ch1.Prob.CT.Mean.out <- renderUI({
if (is.na(input$Ch1.Prob.CT.Mean.in)) { # Error checking
p("No answer provided")
} else if (round(input$Ch1.Prob.CT.Mean.in, digits = 4) == round(mean(Ch1.Prob.CT.data[,1]), digits = 4)) {
p("Correct", style = "color:green")
} else {
p("Incorrect", style = "color:red")
}
})
})
The problem is that the sample is not random; it is always the same, every time. Even when I press the reset button, the sample is exactly the same as the one before.
Why is Shiny not randomizing? And how can I make it randomize again?
Add a line such as
set.seed(as.integer(Sys.time()))
before you need random numbers
Such code:
observeEvent(input$xxx, {
x <- ...
})
f <- eventReactive(input$xxx, {
[do something with x]
})
does not work.
You can simply remove the observer and do:
f <- eventReactive(input$xxx, {
x <- ...
[do something with x]
})
If you want to use a variable subject to modifications inside an observer, you have to use a reactive list, like this :
values <- reactiveValues()
values$x <- [initial value of x]
observeEvent(input$xxx, {
values$x <- ...
})
(In addition, don't use some dots (.) in the names of the shiny elements.)

r shiny table not rendering html

I'm creating a table using renderTable but the HTML inside the table is not rendering:
This is the code snipit of interest:
if (is.null(Compare_Count) || is.na(Compare_Count) || length(Compare_Count) == 0L ) {
CT_Table[i, 3] <- HTML("<i class='icon-arrow-up'></i>")
} else if (CT_Table[i, 2] > Compare_Count) {
CT_Table[i, 3] <- print(tags$i(class='icon-arrow-up', style="text-color: green"), quote = FALSE)
}
Neither HTML, paste, or c work.
How can I get the arrows to show?
Thanks!
server.r: [Note, this is an example. The code is not complete, brackets may be mismatched, etc. Not important to the question.]
output$example <- renderTable(include.rownames=FALSE,{
CT_Table <- count(Canidates,vars=c("Name"))
CT_Table <- CT_Table[order(CT_Table["Recent Reviews: "], decreasing=T),]
for (i in 1:nrow(CT_Table)) {
Compare_Name <- paste(CT_Table$Product[i])
Compare_Count <- Can_trend[Can_trend$Name == Compare_Name, 2]
if (is.null(Compare_Count) || is.na(Compare_Count) || length(Compare_Count) == 0L )
{
CT_Table[i, 3] <- HTML("<i class='icon-arrow-up'></i>")
} else if (CT_Table[i, 2] > Compare_Count) {
CT_Table[i, 3] <- tags$i(class='icon-arrow-up', style="text-color: green")
} else if (CT_Table[i, 2] < Compare_Count) {
CT_Table[i, 3] <- tags$i(class='icon-arrow-down', style="text-color: red")
} else if (CT_Table[i, 2] == Compare_Count) {
CT_Table[i, 3] <- tags$i(class='icon-minus', style="text-color: yellow")
}
}
}
CT_Table
})
ui.r is just a simple call to tableOutput or htmlOutput, but neither renders the html pasted into the column.
This was fixed with sanitize.text.function = function(x) x;
it needs to be included like this:
output$example <- renderTable({
table <- someTable_Data_here
table
}, sanitize.text.function = function(x) x)
This is the gist here
also, a note,
I have noticed that you can call xtable inside the renderTable function, and it will properly render the table.
BUT you should note that options you pass to xtable have no effect! Instead you need to pass those options to the 'renderTable' function.
so if you want to call this:
output$example <- renderTable({
table <- someTable_Data_here
xtable(table, align=c("llr"))
}, sanitize.text.function = function(x) x)
what you need to do is:
output$example <- renderTable({
table <- someTable_Data_here
table
},align=c("llr"), sanitize.text.function = function(x) x)
The RStudio team and the RShiny guys are awesome. I'm sure a ton of the documentation is still being written, and I hope this helps someone in the mean time.

Resources