I've a problem with my shiny code which is somewhat related with the two questions I linked below.
So i've made a dynamic UI :
output$hmgroupsmean <-renderUI({
numGroups <- as.integer(input$HMgroups)
lapply(1:numGroups, function(i) {
numericInput(paste0("group_", i),
label= paste("Mean", i),
min=0,
max=1000,
value= 10)
})
})
that display N numericInput depending on how many groups the user has chosen.
Then I just want to retrieve all the mean and there begin the problem :
I tried what is explained here but it doesnt work :
output$PowerAnalysisANOVA <- renderPlot({
allmean = c()
lapply(1:numGroups, function(i){
allmean[i] <- input[[paste0("group_", i)]]
})
qplot(allname)
})
it returns :
Error : argument "env" is missing, with no default
then i tried something a tad more exotic :
output$PowerAnalysisANOVA <- renderPlot({
allname = c()
allmean = c()
lapply(1:numGroups, function(i){
allname[i] <- paste0("input$group_" ,i)
allmean[i] <- get(allname[i])
})
qplot(allmean)
})
But it doesn't work : Error :object 'input$group_1' not found
Edit after AndriyTkach's comment :
output$PowerAnalysisANOVA <- renderPlot({
allname = c()
allmean = c()
lapply(1:numGroups, function(i){
allname[i] <- eval(parse (text = paste0("input$group_" ,i)))
allmean[i] <- get(allname[i])
})
qplot(allmean)
})
It returns a new Error : invalid first argument
AndriyTkach proposed that :
for (i in 1:numGroups)
eval (parse (text = paste0("allmean[", i, "] <- input$group_" ,i)))
Which work a lot better : no Error message but it only work for 2 and 3 groups , the fourth and up is not taken into account
Create dynamic number of input elements with R/Shiny
accessing inputs created in renderUI in Shiny
After some discussion with AndriyTkach : I have a working program :
output$PowerAnalysisANOVA <- renderPlot({
allmean = c()
for (i in 1:values$numGroups)
eval (parse (text = paste0("allmean[", i, "] <- input$group_" ,i)))
qplot(allmean)
})
And I had forgotten to make a reactive variable :
values <- reactiveValues()
output$hmgroupsmean <-renderUI({
values$numGroups <- as.integer(input$HMgroups)
apply(1:values$numGroups, function(i) {
numericInput(paste0("group_", i),
label= paste("Condition", i),
min=0,
max=1000,
value= 10)
})
})
Related
The below code runs fine so long as the line in server section v <- reactiveValues(results=tibble(Scenario = 1, data())) is commented out. When I uncomment it the App crashes and I get the error message: Warning: Error in : Can't access reactive value 'input1' outside of reactive consumer. i Do you need to wrap inside reactive() or observer()?
I'm trying to create a vector using tibble for another function to be added. What am I doing wrong here in my use of tibble? I'm trying to capture via tibble, as a vector, the values generated by my custom interpol function when it takes inputs from matrix input2 through the data() function below. I also tried c(), as.vector(), etc., to make sure input2 is converted to a vector but I still get the same error.
I'm completely new to tibbles and tidyverse etc.
Code:
library(shiny)
library(shinyMatrix)
library(tidyverse) # < ADDED
interpol <- function(a,b){ # a = periods, b = matrix inputs
c <- rep(NA,a)
c[1] <- b[1]
c[a] <- b[2]
c <- approx(seq_along(c)[!is.na(c)],c[!is.na(c)],seq_along(c))$y # this interpolates
return(c)
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(uiOutput("panel"),actionButton("showInput2","Modify/add interpolation")),
mainPanel(plotOutput("plot"))
)
)
server <- function(input, output, session){
data <- function(){
if(!isTruthy(input$input1)){interpol(6,matrix(c(1,5)))} else {
if(!isTruthy(input$input2)){interpol(input$periods,
matrix(c(input$input1[1,1],input$input1[1,2])))} else {
interpol(input$periods,matrix(c(input$input2[1,1],input$input2[1,2])))}}
}
# v <- reactiveValues(results=tibble(Scenario = 1, data()))
output$panel <- renderUI({
tagList(
sliderInput('periods','Interpolate over periods (X):',min=2,max=12,value=6),
uiOutput("input1"))
})
output$input1 <- renderUI({
matrixInput("input1",
label = "Interpolation 1 (Y values):",
value = matrix(if(isTruthy(input$input2)){c(input$input2[1],input$input2[2])}
else {c(1,5)}, # matrix values
1, 2, # matrix row/column count
dimnames = list(NULL,c("Start","End"))), # matrix column header
rows = list(names = FALSE), class = "numeric")
})
observeEvent(input$showInput2,{
showModal(
modalDialog(
matrixInput("input2",
label = "Automatically numbered scenarios (input into blank cells to add):",
value = if(isTruthy(input$input2)){input$input2}
else if(isTruthy(input$input1)){input$input1},
rows = list(names = FALSE),
cols = list(extend = TRUE, delta = 2, delete = TRUE, multiheader=TRUE),
class = "numeric"),
footer = modalButton("Close")
))
})
observe({
req(input$input2)
mm <- input$input2
colnames(mm) <- paste(trunc(1:ncol(mm)/2)+1, " (start|end)")
isolate(updateMatrixInput(session, "input2", mm))
})
output$plot<-renderPlot({plot(data(),type="l",xlab="Periods (X)", ylab="Interpolated Y values")})
}
shinyApp(ui, server)
The reason for the error and solution actually is present in the error message itself. You cannot access reactive variable (data()) outside reactive context. Try wrapping the v output in reactive.
v <- reactive({tibble(Scenario = 1, data())})
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)
}
}
})
I'm working on a some text mining. Based on the user's input I'm generating a number of suggestions for the next word. This part works fine. However the number of suggestions can be very large, so I want to show at most 10 suggestions in Shiny and I don't want to show NA values.
I created a reproducable example to exhibits the same problem. The trick I'm trying to use is pasting "suggestions" with i. This works when my output does not depend on my input. I got this from http://shiny.rstudio.com/gallery/creating-a-ui-from-a-loop.html.
My ui.R file
library(shiny)
fluidPage(
titlePanel("Test"),
fluidRow(
textAreaInput("userText", label="Enter your text")
),
fluidRow(
lapply(1:5, function(i) {
textOutput(paste0("suggestions", i))})
)
)
My server.R
library(shiny)
mySuggestions <- c("this", "is", "a", "test", "of", "getting", "reactive", "list", "length")
function(input, output, session) {
getWords <- function(i, wrds) {
output[[paste0("suggestions", i)]] <- renderText({ wrds()[i] })
}
userText <- reactive({
# Leaves this function when input$userText is NULL or ""
req(input$userText)
input$userText })
words <- reactive({
mySuggestions[1:userText()]
})
# Problem
lapply(reactive({ 1:min(5, length(words())) }), getWords(), wrds=words())
}
When you enter a positive integer in the ui text field the app is supposed to show as many words, but 5 at most.
The above version of the server.R results in a warning "Warning: Error in paste0: argument "i" is missing, with no default"
I've tried several versions for this problematic line.
reactive({ lapply(1:min(5, length(words())), getWords(), wrds=words() ) })
Gives no errors, but it shows nothing in the output.
lapply(1:min(5, length(words())), getWords() , wrds=words())
Results in a warning "Warning: Error in paste0: argument "i" is missing, with no default"
lapply(reactive({1:min(5, length(words()))}), getWords(), wrds=words())
Results in a warning "Warning: Error in paste0: argument "i" is missing, with no default"
lapply(reactive({1:min(5, length(words))}), function(i) {
output[[paste0("suggestions", i)]] <- renderText({ words[i] }) } )
Results in Error in as.vector(x, "list") :
cannot coerce type 'closure' to vector of type 'list'
lapply(reactive({1:min(5, length(words()))}), function(i) {
output[[paste0("suggestions", i)]] <- renderText({ words()[i] }) } )
Results in Error in as.vector(x, "list") :
cannot coerce type 'closure' to vector of type 'list'
reactive({lapply(1:min(5, length(words)), function(i) {
output[[paste0("suggestions", i)]] <- renderText({ words[i] }) }) })
Gives no errors, but it shows nothing in the output.
reactive({lapply(1:min(5, length(words())), function(i) {
output[[paste0("suggestions", i)]] <- renderText({ words()[i] }) }) })
Gives no errors, but it shows nothing in the output.
lapply(1:min(5, reactive({ length(words )})), function(i) {
output[[paste0("suggestions", i)]] <- renderText({ words[i] }) })
Results in Error in min(5, reactive({ : invalid 'type' (closure) of argument
lapply(1:min(5, reactive({ length(words() )})), function(i) {
output[[paste0("suggestions", i)]] <- renderText({ words()[i] }) })
Results in Error in min(5, reactive({ : invalid 'type' (closure) of argument
Now the following line shows the entered number of words in a single text field. When I enter 2 it shows 2 words and when I enter 20 it shows 5 words. This is the behaviour I want, but I want each word in a separate text field.
output$suggestions1 <- renderText(words()[1:min(5, length(words()))])
I'm getting lost. I was getting so desperate that I tried a few things I did not expect to work.
Is it possible to do what I want? If so, how? If not, what is the problem? I haven't found anything yet that addresses this specific problem.
The combination of outputUI and renderUI works great and keeps the code relatively simple.
ui.R
...
fluidRow(
uiOutput("suggestions")
)
server.R
library(shiny)
mySuggestions <- c("this", "is", "a", "test", "of", "getting", "reactive", "list", "length")
function(input, output, session) {
...
output$suggestions <- renderUI({
lapply(1:min(5, length(words())), function(i) {
output[[paste0("suggestions", i)]] <- renderText({ words()[i] })
}) })
}
I didn't know what outputUI and renderUI did, but they seem perfect for situations like these.
I have successfully updated UI dynamically through renderUI(). I have a long list of inputs to choose from. The check boxes are used to dynamically add numeric inputs. So, to implement this, I used lapply. However, I have used values of selected check boxes in checkboxgroup itself to populate IDs of the dynamically added numerical input instead of using paste(input, i) in lapply.
ui code snippet :
checkboxGroupInput(inputId = "checkboxgrp", label = "Select types",
choices = list("ELECTAPP","NB W $","PUR","MANUAL LTR","REDEMPTION","NB W TRANSFER","NB WOUT $","OUTPUT")),
...
fluidRow(column(12, verbatimTextOutput("value")))
...
uiOutput("numerics")
server code snippet :
renderUI({
numInputs <- length(input$checkboxgrp)
if(numInputs==0){
wellPanel("No transaction selected")
}
else{
lapply(1:numInputs, function(i){
x[i]=input$checkboxgrp[i]
list(numericInput(input$checkboxgrp[i], min = 0, label = input$checkboxgrp[i],
value= input[[x[i]]] ))
})
}
})
output$value <- renderPrint({
numInputs <- length(input$checkboxgrp)
lapply(1:numInputs, function(i){
print(input[[x[i]]]) ## ERROR
})
})
I have used input[[x[i]]] as to instantiate value to be retained after adding or removing a numeric input. But, I want to extract values from input$x[i] or input[[x[i]]] into a vector for further use which I'm unable to do.
*ERROR:Must use single string to index into reactivevalues
Any help is appreciated.
EDIT
using 3 different ways of extracting values from input generate 3 different errors:
Using print(input$x[i]) # ERROR
NULL
NULL
NULL
NULL
[[1]]
NULL
[[2]]
NULL
[[3]]
NULL
[[4]]
NULL
Using print(input[[x[i]]]) # ERROR
Must use single string to index into reactivevalues
Using print('$'(input, x[i])) # ERROR
invalid subscript type 'language'
If I understand you correctly, you want to access values of dynamically generated widgets and then just print them out.
In my example below, which should be easy to generalise, the choices are the levels of the variable Setosa from the iris dataset.
The IDs of the generated widgets are always given by the selected values in checkboxGroupInput. So, input$checkboxgrp says to shiny for which level of setosa there should be generated a widget. At the same time input$checkboxgrp gives IDs of generated widgets. That's why you don't need to store the IDs of "active" widgets in other variable x (which is probably a reactive value).
To print the values out you can do the following:
output$value <- renderPrint({
activeWidgets <- input$checkboxgrp
for (i in activeWidgets) {
print(paste0(i, " = ", input[[i]]))
}
})
This line print(input[[x[i]]]) ## ERROR yields an error because x[i] (whatever it is) is not a vector with a single value but with multiple values.
Full example:
library(shiny)
ui <- fluidPage(
titlePanel("Old Faithful Geyser Data"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkboxgrp", "levels", levels(iris$Species))
),
mainPanel(
fluidRow(
column(6, uiOutput("dynamic")),
column(6, verbatimTextOutput("value"))
)
)
)
)
server <- function(input, output) {
output$dynamic <- renderUI({
numInputs <- length(input$checkboxgrp)
if(numInputs==0){
wellPanel("No transaction selected")
}
else{
lapply(1:numInputs, function(i){
x[i]=input$checkboxgrp[i]
list(numericInput(input$checkboxgrp[i], min = 0, label = input$checkboxgrp[i],
value= input[[x[i]]] ))
})
}
})
output$value <- renderPrint({
activeWidgets <- input$checkboxgrp
for (i in activeWidgets) {
print(paste0(i, " = ", input[[i]]))
}
})
}
shinyApp(ui = ui, server = server)
Edit:
You could tweak the lapply part a little bit (mind <<- operator :) )
else{
activeWidgets <- input$checkboxgrp
val <- 0
lapply(activeWidgets, function(i){
val <<- val + 1
list(numericInput(i, min = 0, label = i,
value = val ))
})
}
Edit 2 In response to a comment:
server <- function(input, output) {
output$dynamic <- renderUI({
numInputs <- length(input$checkboxgrp)
if(numInputs==0){
wellPanel("No transaction selected")
}
else{
activeWidgets <- input$checkboxgrp
val <- 0
lapply(activeWidgets, function(i){
val <<- val + 1
list(numericInput(i, min = 0, label = i,
value = val ))
})
}
})
allChoices <- reactive({
# Require that all input$checkboxgrp and
# the last generated numericInput are available.
# (If the last generated numericInput is available (is not NULL),
# then all previous are available too)
# "eval(parse(text = paste0("input$", input$checkboxgrp))))" yields
# a value of the last generated numericInput.
# In this way we avoid multiple re-evaulation of allChoices()
# and errors
req(input$checkboxgrp, eval(parse(text = paste0("input$", input$checkboxgrp))))
activeWidgets <- input$checkboxgrp
res <- numeric(length(activeWidgets))
names(res) <- activeWidgets
for (i in activeWidgets) {
res[i] <- input[[i]]
}
res
})
output$value <- renderPrint({
print(allChoices())
})
}
I'm using Shiny's renerUI to create UI objects on the fly following instruction from this question.
i'm getting the following error
Error in if (!grepl(pattern, text)) return(text) :
argument is of length zero
after reading this i know the problem is this line
# Convert the list to a tagList
do.call(tagList, plot_output_list)
but i'm doing exactly what the solution suggested.
appreciate any help.
the code
Server.r
shinyServer(function(input, output) {
# Create an environment for storing data
symbol_env <- new.env()
output$plots <- renderUI({
if (input$goButton == 0)
return()
if (loaded_index != input$chosen_index) {
# ... alot of code to generate data.frames and plots
output[["summary"]] <- renderPlot(grid.arrange(g1,g2,g3,g4,g5,nrow=1))
for (i in 1:length(results)) {
# Need local so that each item gets its own number. Without it, the value
# of i in the renderPlot() will be the same across all instances, because
# of when the expression is evaluated.
local({
my_i <- i
plotname <- object_name("plot", names(results[my_i]))
output[[plotname]] <- renderPlot({
showme_plot(results[[names(results[my_i])]]$data , period = DEFAULT_TIME_PERIOD)})
tablename <- object_name("table", names(results[my_i]))
output[[tablename]] <- renderTable({
make_summary(names(results[my_i]))})
})
}
}
plot_output_list <- list()
# Graphical Summary
plot_output_list[["summary"]] <- tags$div(class="summary" , plotOutput("summary"))
# The List
for (symbol in names(results))
plot_output_list[[symbol]] <-
tags$div(class = "container" ,
tags$div(class = "name" , name(symbol)),
tags$div(class = "stats" , tableOutput(object_name("table", symbol))) ,
tags$div(class = "plot" , plotOutput(object_name("plot", symbol), height = 530)))
print("Plot structure defined")
# Convert the list to a tagList
do.call(tagList, plot_output_list)
})
})
ui.r
require(shinyIncubator)
shinyUI(
bootstrapPage(
tags$script(src = "keyboard_scroller.js") ,
tags$link(rel="stylesheet" , type="text/css" , href="custom.css") ,
actionButton("goButton", "Go!") ,
selectInput("chosen_index" , "INDEX: " , list("A" = '1' , "B" = '2')) ,
# This is the dynamic UI for the plots
uiOutput("plots")
))
I would guess one problem is that the plot_output_list should not be indexed by strings, but instead by number, i.e. plot_output_list[[symbol]] <- ... should be plot_output_list[[i]] <- ...; i <- i + 1
If that doesn't work, try running traceback() right after you hit the error so we can see where the error comes from.