My goal is to implement a Shiny app which shows several "problems" to the user with a list of possible answers, then lets the user choose an answer and stores it. Every problem has a previously stored answer that the user can overwrite.
I can't find a way to satisfy these two constraints at the same time with reactive objects:
let the user select the next problem, with the answer being re-initialized from the previously stored answer
store the answer for the current problem when the user selects a new answer (and only in this case)
Below a simplified code (no data, no loading/writing) which shows my current attempt. In this version the issue is that when a new problem is selected, the selected answer from the previous problem is immediately written.
library(shiny)
maxProblem=10
ui <- fluidPage(
titlePanel("Debugging test"),
fluidRow(
column(12,
verbatimTextOutput("nbProblems"),
uiOutput("ProblemSelection"),
uiOutput("answerSelection")
)
)
)
server <- function(input, output) {
output$ProblemSelection <- renderUI({
numericInput("ProblemSelectionNo",
"Select Problem no",
value = 1, min=1, max=maxProblem)
})
currentProblemData <- reactive({
print("calling loadCurrentProblemData")
if (!is.null(input$ProblemSelectionNo)) {
print("pretending to load data and previously stored answer for problem", input$ProblemSelectionNo)
list( choices=c(1,2,3), answer=1)
}
})
output$answerSelection <- renderUI({
l<-currentProblemData()
choicesList <- l$choices
names(choicesList) <- l$choices
radioButtons("answerInput", label = "Select answer",
choices = choicesList,
selected = l$answer)
})
writeChanges <- observe({
print('calling writeChanges')
l<-currentProblemData()
newAnswer <- input$answerInput
prevAnswer <- l$answer
if (!is.null(prevAnswer) && !is.null(newAnswer) && (newAnswer != prevAnswer)) {
print(paste('Pretending to write new answer :',newAnswer,'for problem', input$ProblemSelectionNo))
l$answer <- newAnswer
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
This might have the functionality you are looking for. I made a workable example to try based on some of what you have.
First, I created a default list choices_answer which could flexibly store your default problem choices and answers. A reactiveValues list lst (rv$lst) will start with this, and then change as new answers are selected to store the new responses.
When a new problem is selected through the numericInput, then the radioButtons are updated based on the current answer for that problem (using the rv$lst). Likewise, when a new answer is chosen (or answer is changed), the rv$lst will be updated with the new answer for storage.
I also added output ListData to show what the storage of answers looks like as you make selections using the radio buttons.
library(shiny)
maxProblem = 5
choices_answer = list()
for (i in seq_along(1:maxProblem)) {
choices_answer[[i]] <- list(
choices = c("1", "2", "3"),
answer = "1"
)
}
ui <- fluidPage(
titlePanel("Debugging test"),
fluidRow(
column(12,
numericInput("ProblemSelectionNo",
"Select Problem no",
value = 1, min = 1, max = maxProblem),
radioButtons("answerInput", label = "Select answer",
choices = choices_answer[[1]][["choices"]]),
verbatimTextOutput("ListData")
)
)
)
server <- function(input, output, session) {
rv <- reactiveValues(lst = choices_answer)
observeEvent(input$ProblemSelectionNo, {
updateRadioButtons(session, "answerInput",
choices = rv$lst[[input$ProblemSelectionNo]][["choices"]],
selected = rv$lst[[input$ProblemSelectionNo]][["answer"]])
})
observeEvent(input$answerInput, {
rv$lst[[input$ProblemSelectionNo]][["answer"]] <- input$answerInput
})
output$ListData <- renderPrint({rv$lst})
}
# Run the application
shinyApp(ui = ui, server = server)
Related
I would like to provide reactively drop down choices (autocomplete suggestions) to the user depending on the input he provides. User input and autocomplete suggestions should be in the same widget.
The problem is, that I have a large data set (1 million rows+) from which the user should be able to choose names from. Since preloading this large data set would slow down the app, I would like to work with multiple splitted data sets, which I load depending on first letter of the user input.
The goal is to provide something like a textInput widget where the user is able to start typing a name. Depending on the first letter of this user input, I would like to present suggestions to the user from which he should be able to choose from.
Unfortunately this is not possible with:
textInput (lacks of drop down with suggestions)
selectInput / selectizeInput / select2Input (lacks of ability to work with non-pre loaded data)
Here are my failed trails with different input widgets to get this to work.
library(shiny)
library(shinysky)
#ui
ui <- fluidPage(
#Text Input
textInput(inputId = "txtInput", label = "Text Input", value = NULL),
#Select Input
selectInput(inputId = "selectInput", label = "Select Input", choices = NULL),
#Selectize Input
selectizeInput(inputId = "selectizeInput", label = "Selectize Input", choices = NULL),
#Select2Input from shinysky
select2Input(inputId = "select2Input", label = "Select2 Input", choices = NULL)
)
#server
server <- function(input, output, session) {
#Create reactive values
rv <- reactiveValues()
#Function for loading specific data set
f.populate.values <- function(start.letter) {
data.set <- c()
if(start.letter == "a") {
#Example toy list
data.set <- c("aaa", "abc", "adb", "adf")
}
if(start.letter == "b") {
#Example toy list
data.set <- c("baa", "bbc", "bdb", "bdf")
}
return(data.set)
}
#Failed trials to update / generate a dropdown with the loaded data set from "f.populate.values"
observeEvent(input$txtInput, {
#Store loaded data depending on first typed character
rv$names <- f.populate.values(substring(input$txtInput, 1, 1))
#Trials to update suggestions in widgets
updateTextInput(inputId = "txtInput", value = rv$names, session = session)
updateSelectInput(inputId = "selectInput", choices = rv$names, session = session)
updateSelectizeInput(inputId = "selectizeInput", choices = rv$names, session = session)
updateSelect2Input(inputId = "select2Input", choices = rv$names, label = "Select 2 Input", session = session)
})
}
shinyApp(ui, server)
I am new to RShiny. I want to populate RShiny dropdowns based previous selections.
For E.g. in the image below,
User first selects the 'route', upon which 'schedule' drop-down gets populated, then user selects 'schedule', then 'trip' drop-down is populated and user selects a 'trip' input.
This is my code:
library(shiny)
library("plotly")
library(lubridate)
require(rgl)
require(akima)
library(dplyr)
library(DT)
data335 <<- read.csv("final335eonly.csv")
#data335[c(2,4,5,8,9,10)] = lapply(data335[c(2,4,5,8,9,10)], as.numeric)
routes <<- as.vector(unique(data335[,'route_no']))
ui <- fluidPage(
titlePanel("Demand Analysis"),
selectInput("routeInput", "Select the route", choices = routes),
selectInput("scheduleInput", "Select the schedule", c("")),
selectInput("tripInput", "Select the trip", c(""))
)
server <- function(input, output, session) {
observeEvent(input$routeInput,
{
x <<- input$routeInput
updateSelectInput(session, "scheduleInput",
choices = data335[data335$route_no == input$routeInput, ]$schedule_no,selected = tail(x, 1)
)
}
)
observeEvent(input$scheduleInput,
{
y <<- input$scheduleInput
updateSelectInput(session, "tripInput",
choices = data335[(data335$route_no == input$routeInput & data335$schedule_no == input$scheduleInput), ]$trip_no,selected = tail(y, 1)
)
}
)
}
shinyApp(ui = ui, server = server)
The input csv file required is here:
Whenever I try to run this seemingly simple code, eventhough the UI appears, when I try to select the inputs in dropdown, RShiny crashes.
Can you please let me know what is causing this?
The problem is happening because you are not giving unique values as choices. data335[data335$route_no == input$routeInput, ]$schedule_no have duplicate values which causes the crash.
Also, you are selecting the value of input$routeInput in your scheduleInput, which is not listed in the choice could be another reason for the crash.
Just commenting the two statements and adding unique to your choices resolves the crash.
Also as #parth pointed out in his comments why are you using <<- everywhere in your code, it not necessary. Although its not the cause of the crash, until and unless you want to share variables between sessions use of <<- inside the server is not a good practice.
Here is your code with the commented section with two selected arguments commented and unique added that works:
library(shiny)
library("plotly")
library(lubridate)
require(rgl)
require(akima)
library(dplyr)
library(DT)
data335 <<- read.csv("final335eonly.csv", stringsAsFactors = FALSE)
routes <<- as.vector(unique(data335[,'route_no']))
ui <- fluidPage(
titlePanel("Demand Analysis"),
selectInput("routeInput", "Select the route", choices = routes),
selectInput("scheduleInput", "Select the schedule", c("")),
selectInput("tripInput", "Select the trip", c(""))
)
server <- function(input, output, session) {
observeEvent(input$routeInput,
{
x <<- input$routeInput
updateSelectInput(session, "scheduleInput",
choices =unique(data335[data335$route_no == input$routeInput, ]$schedule_no),#selected = tail(x, 1)
)
}
)
observeEvent(input$scheduleInput,
{
y <<- input$scheduleInput
updateSelectInput(session, "tripInput",
choices = unique(data335[(data335$route_no == input$routeInput & data335$schedule_no == input$scheduleInput), ]$trip_no),#selected = tail(y, 1)
)
}
)
}
shinyApp(ui = ui, server = server)
We have created a shiny application where either the user can upload a big dataset (RData file over 200MB) or they can pick one from us. Then there are three different tabs where the user can filter the data (tab for numerics, tab for categorics)
So currently I have 3 reactive functions to serve that purpose. But downside is that the object is kept three times in memory. Is there a more efficient way to do this?
Please find a simplified example app below:
note: in this app you only see 1 filter per tab. normally its more like this:
My_Filtered_Data[Species %in% input$filter1 &
x %in% input$x &
y %in% input$y &
z %in% input$z] #etc.
I was looking at reactiveValues but couldn't really find how it works.
Reason I don't want to have it in 1 reactive is that everytime I change one of the filters on one of the sheets, the entire filtering process starts again and that is quite time consuming. I'd prefer to have one dataset that that gets updated with only the filter that is used at that time. That's the reason I included the different reactives
## app.R ##
library(shinydashboard)
library(data.table)
CustomHeader <- dashboardHeader(title='datatest')
iris<-iris
ui <- function(request) {
dashboardPage(
CustomHeader,
## Sidebar content
dashboardSidebar(
sidebarMenu(
menuItem("filter1 & Import", tabName = "filter1", icon = icon("dashboard")),
menuItem("filter2", tabName = "filter2", icon = icon("th")),
menuItem("filter3", tabName = "filter3", icon = icon("th"))
)
),
## Body content
dashboardBody(
tabItems(
# First tab content
tabItem(tabName = "filter1",
fluidRow(box(width = 3,
selectInput(inputId = 'filter1','filter1:species',choices = unique(iris$Species))))
),
tabItem(tabName = "filter2",
fluidRow(box(width = 3,
sliderInput(inputId = 'filter2','filter2:Max.Sepal.Length',min = 0,max = 10,value = 10)
))
),
tabItem(tabName = "filter3",
fluidRow(box(width = 3,
sliderInput(inputId = 'filter3','filter3:Min.Sepal.Width',min = 0,max = 10,value = 0)
),
box(width=9,dataTableOutput('mydata')))
)
)
)
)
}
server <- function(input, output) {
My_Uploaded_Data <- reactive({
My_Uploaded_Data<-data.table(iris)
My_Uploaded_Data
})
My_Filtered_Data <- reactive({
My_Filtered_Data<-My_Uploaded_Data()
My_Filtered_Data[Species %in% input$filter1]
})
My_Filtered_Data2 <- reactive({
My_Filtered_Data2<-My_Filtered_Data()
My_Filtered_Data2[Sepal.Length < input$filter2]
})
My_Filtered_Data3 <- reactive({
My_Filtered_Data3<-My_Filtered_Data2()
My_Filtered_Data3[Sepal.Width > input$filter3]
})
output$mydata<-renderDataTable({
My_Filtered_Data3()
})
}
shinyApp(ui, server)
I was hoping something like tthis would work in reactiveValues
react_vals <- reactiveValues(data = NULL)
observe(react_vals$data <- MyLoadedData())
observe(react_vals$data <- react_vals$data[Species %in% input$filter1])
observe(react_vals$data <- react_vals$data[Sepal.Length < input$filter2])
observe(react_vals$data <- react_vals$data[Sepal.Width > input$filter3])
EDIT: I also would like to include bookmarks: https://shiny.rstudio.com/articles/advanced-bookmarking.html and it seems you need reactiveValues for that. So another reason for me to move away from all these reactives/eventReactive
Instead of storing datasets in the reactive variables, just store the rows which qualify. That way, each reactive value is only replaced when it's filter changes; they aren't linked together. The output just uses the rows which pass all filters.
At the top of the program, change iris to a data.table:
library(shinydashboard)
library(data.table)
CustomHeader <- dashboardHeader(title = 'datatest')
iris <- iris
setDT(iris) # Added
Then use this for the server logic:
server <- function(input, output) {
filter1_rows <- reactive({
iris[Species %in% input$filter1, which = TRUE]
})
filter2_rows <- reactive({
iris[Sepal.Length < input$filter2, which = TRUE]
})
filter3_rows <- reactive({
iris[Sepal.Width > input$filter3, which = TRUE]
})
output$mydata <- renderDataTable({
final_rows <- intersect(filter1_rows(), filter2_rows())
final_rows <- intersect(final_rows, filter3_rows())
iris[final_rows]
})
}
This uses the often-overlooked which argument for data.table[...], which means only the row numbers of the subsetted table should be returned.
I think your problem has nothing to do with shiny and/or reactive programming. It's a "classic time vs memory" situation. Basically speaking you have only two options: Store "partially" filtered objects or not.
If you do store them, you use a lot of memory but can return the object instantly. If not, you need only store the original object but you have to filter it everytime again. There is nothing in between. You just cannot create an object that is different from the original (i.e. filtered) but takes no additional memory, not even with reactiveValues.
Of course you can do tradeoffs, e.g. creating an intermediate object for the first filter and computing the second and the third filter on-the-fly, but that does not change the underlying problem.
I have a shiny application with many tabs and many widgets on each tab. It is a data-driven application so the data is tied to every tab.
I can save the application using image.save() and create a .RData file for later use.
The issue I am having how can I get the state restored for the widgets?
If the user has checked boxes, selected radio buttons and specified base line values in list boxes can I set those within a load() step?
I have found libraries such as shinyURL and shinystore but is there a direct way to set the environment back to when the write.image was done?
I am not sure where to even start so I can't post code.
edit: this is a cross-post from the Shiny Google Group where other solutions have been suggested
This is a bit hacky, but it works. It uses an "internal" function (session$sendInputMessage) which is not meant to be called explicitly, so there is no guarantee this will always work.
You want to save all the values of the input object. I'm getting all the widgets using reactiveValuesToList(input) (note that this will also save the state of buttons, which doesn't entirely make sense). An alternative approach would be to enumerate exactly which widgets to save, but that solution would be less generic and you'd have to update it every time you add/remove an input. In the code below I simply save the values to a list called values, you can save that to file however you'd like (RDS/text file/whatever). Then the load button looks at that list and updates every input based on the value in the list.
There is a similar idea in this thread
library(shiny)
shinyApp(
ui = fluidPage(
textInput("text", "text", ""),
selectInput("select", "select", 1:5),
uiOutput("ui"),
actionButton("save", "Save"),
actionButton("load", "Load")
),
server = function(input, output, session) {
output$ui <- renderUI({
tagList(
numericInput("num", "num", 7),
checkboxGroupInput("chk", "chk", 1:5, c(2,4))
)
})
observeEvent(input$save, {
values <<- lapply(reactiveValuesToList(input), unclass)
})
observeEvent(input$load, {
if (exists("values")) {
lapply(names(values),
function(x) session$sendInputMessage(x, list(value = values[[x]]))
)
}
})
}
)
Now with bookmarking is possible to save the state of your shinyapp. You have to put the bookmarkButton on your app and also the enableBookmarking.
The above example may not work if shiny UI involves date. Here is a minor change for date handling.
library(shiny)
shinyApp(
ui = fluidPage(
dateInput("date", "date", "2012-01-01"),
selectInput("select", "select", 1:5),
uiOutput("ui"),
actionButton("save", "Save"),
actionButton("load", "Load")
),
server = function(input, output, session) {
output$ui <- renderUI({
tagList(
numericInput("num", "num", 7),
checkboxGroupInput("chk", "chk", 1:5, c(2,4))
)
})
observeEvent(input$save, {
values <<- lapply(reactiveValuesToList(input), unclass)
})
observeEvent(input$load, {
if (exists("values")) {
lapply(names(values),
function(x) session$sendInputMessage(x, list(value = values[[x]]))
)
temp=as.character(as.Date(values$date, origin = "1970-01-01"))
updateDateInput(session, inputId="date", label ="date", value = temp)
}
})
}
)
Typically in a web interface if you have a dropdown populated from a database that display's some text and you want to use that selected text in the dropdown and pass it back to a database. But a lot of times you want to pass an ID instead of the actual text displayed.
In my example below I have a global.R file that returns the data for the dropdowns. This simulates data returned from a database. For each dropdown there is a text field that is displayed in the dropdowns and an "id" field that is not displayed BUT I have to somehow access the "id" fields of the dropdowns. How is this done in Shiny?... Because the selectInputs don't allow you to store the ids so you can access them like input$DisplayName$id
In the example below I just want to print the "id" of the "DisplayName" selectInput so if "Mary" is in the input$DisplayName then "20" should be printed in the RenderText call.
Here is code to run:
require(shiny)
runApp(list(
ui = basicPage(
sidebarPanel(
selectInput("Department", "Select a department", choices = as.character(GetDepartments()$Department), selected = as.character(GetDepartments()$Department[1])),
uiOutput("DisplayName")
),
mainPanel(textOutput("Text") )
),
server = function(input, output, session) {
output$DisplayName<-renderUI({
Department <- input$Department
print(Department)
selectInput("DisplayName", 'DisplayName:', choices = as.character(GetDisplayName(Department)$DisplayName), selected =as.character(GetDisplayName(Department)$DisplayName[1] ))
})
output$Text <- renderText({
# Here I want to simulate accessing the "id" field of the input$DisplayName
#in my app I need to pass the id to a database query
#If Mary is in input$DisplayName how can I access her id of "20"?
print("in render text")
return( ??? How do I access the id = 20???)
})
}
))
Here is the global.r file that simulates code that returns stuff from a database
GetDepartments<- function(){
df<- data.frame(Department= c("Dept A", "Dept B"), id = c(1,2))
return(df)
}
GetDisplayName<- function(Dept){
if(Dept == "Dept A")
{
df<- data.frame(DisplayName= c("Bob", "Fred"), id = c(4,6))
return(df)
}else
{
df<- data.frame(DisplayName= c("George", "Mary"), id = c(10,20))
return(df)
}
}
This is very similar to your other question here. As #nrussel suggests, this is just a simple subsetting problem. Just pull up your department and index on the name. Here is a working example.
EDIT*** - make dataset reactive to avoid repetition.
As per the documentation:
Reactive expressions are a smarter than regular R functions. They cache results and only update when they become obsolete. The first time that you run a reactive expression, the expression will save its result in your computer’s memory. The next time you call the reactive expression, it can return this saved result without doing any computation (which will make your app faster). The reactive expression will use this new copy until it too becomes out of date.
runApp(list(
ui = basicPage(
sidebarPanel(
selectInput("Department", "Select a department",
choices = as.character(GetDepartments()$Department),
selected = as.character(GetDepartments()$Department[1])),
uiOutput("DisplayName")
),
mainPanel(textOutput("Text") )
),
server = function(input, output, session) {
myData <- reactive({
GetDisplayName(input$Department)
})
output$DisplayName<-renderUI({
Department <- input$Department
print(Department)
myData <- myData()
selectInput("DisplayName", 'DisplayName:', choices = as.character(myData$DisplayName),
selected =as.character(myData$DisplayName[1] ))
})
output$Text <- renderText({
print("in render text")
myData <- myData()
code <- as.character(myData[myData$DisplayName == input$DisplayName,2])
return(code)
})
}
))