I made an app that takes user input of a googlesheet and boxplots it https://sjgknight.shinyapps.io/boxplotr/ and https://github.com/uts-cic/BoxPlotR.shiny/.
It's mostly working. The inputs aren't reactive, the user has to click a button to read them. But (a) If I have a default 'value' in the input that causes problems, and (b) if a user changes their input and then it gives an error.
E.g. with this data https://docs.google.com/spreadsheets/d/1ycTplwz5q21s1Z3QzpeoP5rMKlkJnQ12GBJJyIU2_q0/ I should be able to create a 'Region' boxplot, and then change the input to 'Income', click the 'click to load data' button again, and I get an error unless I refresh and clear the URL.
Using the guidance at http://shiny.rstudio.com/articles/action-buttons.html I know I can create a 'clear' button that I guess removes all the values, but I'm not clear how I'd go about just re-reading the inputs. I have an isolate block and just tried setting the variables in that to NULL, which doesn't work. Welcome ideas on how to do this. Looking at other questions I'm mostly seeing issues around updating the UI based on user inputs passed to the server.
So, I figure I need to use observeEvent, but this doesn't seem to work:
observeEvent(
input$gloadsheet, {
isolate({
gs_data <- gs_url(input$gsheetURL)
data <- data.frame(gs_read(gs_data, ws = input$gsheetws))
data$nID <- row.names(data)
data <- reshape(data[c("nID",input$ggrouping,input$gdataID)], idvar = "nID", timevar = input$ggrouping, direction = "wide")
data <- data[,-c(1:2)]
data <- data.frame(data)
data
})
}
)
Alternatively, I thought I could use eventReactive per this question Shiny Reactivity Explaination (using ObserveEvent). It may be that the issue is the block this code is based in (which is a reactive to get a variable based on multiple options users can click through).
data <- eventReactive(input$gloadsheet, {
isolate({
gs_data <- gs_url(input$gsheetURL)
data <- data.frame(gs_read(gs_data, ws = input$gsheetws))
data$nID <- row.names(data)
data <- reshape(data[c("nID",input$ggrouping,input$gdataID)], idvar = "nID", timevar = input$ggrouping, direction = "wide")
data <- data[,-c(1:2)]
data <- data.frame(data)
})
data
})
data
You can try this on your server-side:
update <- reactiveValues(please = 0)
observeEvent(input$clearValues, {
updateTextInput(...) # Clear everything #
update$please <- update$please + 1
})
inYourRenderingFunction <- render..({
update$please
.
.
})
You create a reactive value that change after clear your inputs. Your render functions has to react to this reactive value (update$please)
I'm using this already. Hope it helps to you.
Related
I have some code I am trying to write in Shiny. This involves the following steps:
- choosing a date
- checking whether a valid input file exists for the date
- if a valid input file exists, pulling down remote time series ending on that date
- carrying out calculations
- plotting the result of the calculations
The date checking should be done reactively. However, the dime series pull down and calculations take some time. I therefore want this to be done only with a button press.
I have most of this working. However, whilst I can put the first instance of the calculation off until a button press using
"if(input$run_shiny_risk==0){
} else {
#some code
}
I can't stop it from calculating automatically on subsequent instances. In other words, as soon as a valid date is chosen, calculations start automatically. I have tried to isolate the calculations without success. I have also tried to isolate a dummy variable that is driven by the button value, but again no luck. Here is what I have in terms of code:
library(shiny)
ui <- fluidPage(
titlePanel("Risk Dashboard"),
sidebarLayout(
sidebarPanel(
helpText("Effective date"),
dateInput("shiny_end_date",
label = "Date (yyyy-mm-dd):",
value = "2018-12-31"),
actionButton("run_shiny_risk",
label = "Run risk report:"),
textOutput("selected_var")
),
mainPanel(
plotOutput("selected_var1")
)
)
)
server <- function(input, output) {
shiny_risk_test <- 0
output$selected_var <- renderText({
holdings_data_file <-paste(substr(input$shiny_end_date,3,4),substr(input$shiny_end_date,6,7),substr(input$shiny_end_date,9,10),"_Data.csv",sep="")
if(file.exists(holdings_data_file)){
end_date <- input$shiny_end_date
paste("You have selected", end_date)
} else {
"No such file"
}
})
output$selected_var1 <- renderPlot({
holdings_data_file <-paste(substr(input$shiny_end_date,3,4),substr(input$shiny_end_date,6,7),substr(input$shiny_end_date,9,10),"_Data.csv",sep="")
if(file.exists(holdings_data_file)){
if(input$run_shiny_risk==0){
#this stops the chart from being drawn initially...
} else {
plot_data <- cbind(c(1,2,3,4),c(1,2,3,4))
p<-plot(plot_data)
p
}
} else {
}
})
}
shinyApp(ui = ui, server = server)
So, this works until the first click. I can change dates between valid and invalid ones, and no chart is drawn until I hit the button. Then, if I move to an invalid date, the chart vanishes, which is right. But if I go back to a valid date, the chart appears again (and would be recalculated if I had the actual, lengthy code enclosed). And code that just stops calculations until the button is pressed, not only the first time but in subsequent case, would be great. Thank you!
I solved this problem in a previous application via the use of reactive values. Consider the following example:
server <- function(input, output, session) {
output_staging <- reactiveValues()
output_staging$title <- NA
observeEvent(input$updateButton,{ update_title() })
update_title <- function(){
output_staging$title <- paste0("random number ",runif(1))
}
output$title <- renderText(output_staging$title)
}
The renderText output looks at the reactive value stored in output_staging$title. Each time the button is clicked, an update function is called that replaces the reactive value.
Note that I could merge the function update_title and the observeEvent command. But having them separate gives me more flexibility.
In your context, I would recommend separating the data prep and the plot generation in order to use this method. You can use the reactive values at either place:
Button sends data to reactiveValue: data --> reactive --> make plot --> display plot
Button sends plot to reactiveValue: data --> make plot --> reactive --> display plot
But you don't need to use it at both.
I am trying to accomplish something very similar to what this poster asked.
However, some of the values that the user is able to input are lists.
For example, the input value input$file1 has sub-values input$file1$name,
input$file1$size,
input$file1$type, and
input$file1$datatype.
Furthermore, I have two inputs, input$remove and input$force, which can take in a list of strings from a selectInput field.
Lastly, the function that I am using to save the user inputs is saving the action buttons, and doing so in an odd format. These values are not loading properly when utilizing the load action button. Here is a sample of the code I am using to attempt this:
observeEvent(input$load,{
if(!file.exists('inputs.RDS')) {return(NULL)}
savedInputs <- readRDS('inputs.RDS')
inputIDs <- names(savedInputs)
inputvalues <- unlist(savedInputs)
for (i in 1:length(savedInputs)) {
session$sendInputMessage(inputIDs[i], list(value=inputvalues[[i]]) )
}
})
observeEvent(input$save,{
saveRDS( reactiveValuesToList(input) , file = 'inputs.RDS')
})
Thank you in advance for the help!
I'm creating a Shiny app that allows a user to load existing input data from a list. However, what I find is that only the independent inputs are being updated while the inputs that are dependent on other inputs remain blank. Here is the code that updates the inputs upon an observeEvent action button:
observeEvent(input$load_project, { #observes the actionButton load_project
projects = Your_projects() #a reactive with list of input names and values
project_names = projects[[1]]
projects_data = projects[[3]]
project_inputs = projects_data[[input$your_projects]]
nInputs = length(project_inputs)
nameInputs = names(project_inputs)
for(k in 1:4){ #hoping if it loops several times the dependent inputs would
#appear but no luck
for(i in 1:nInputs){ #loop through all of the inputs
Type = typeof(project_inputs[[i]]) #get type of input
if(Type == "character"){
updateTextInput(session,nameInputs[i],NULL,value = project_inputs[[i]])
}
}}
})
The code works for updating independent inputs but not dependent inputs. If I create similar code for observing the event of a "refresh_inputs" button and press it then the dependents inputs are updated. But I'd like an automated way to do it.
Thanks for any help on this!
I am using Shiny as an interface for viewing tables stored locally in a series of .RData files however I am unable to get the table to render.
My server code is like this:
output$table1 <- renderTable({
load(paste0(input$one,"/",input$two,".RData"))
myData})
On the ui side I am simply displaying the table in the main panel.
This other SO question suggests that the issue is that the environment that the data is loaded into goes away so the data isn't there to display. They suggest creating a global file and loading the .RData file in there, but I don't believe I will be able to load the data dynamically that way. Any guidance on how to use .RData files effectively within shiny would be appreciated.
Regards
I think you just need to move the load statement outside of the renderTable function. So you should have
load(paste0(input$one,"/",input$two,".RData"))
output$table1 <- renderTable({myData})
If you look at the help file for renderTable, the first argument is
expr: An expression that returns an R object that can be used with
xtable.
load does not return this.
I got around this by "tricking" R Shiny. I make a BOGUS textOutput, and in renderText, call a external function that, based in the input selected, sets the already globally loaded environments to a single environment called "e". Note, you MUST manually load all RDatas into environments in global.R first, with this approach. Assuming your data isn't that large, or that you don't have a million RDatas, this seems like a reasonable hack.
By essentially creating a loadEnvFn() like the below that returns a string input passed as input$datasetNumber, you can avoid the scoping issues that occur when you put code in a reactive({}) context. I tried to do a TON of things, but they all required reactive contexts. This way, I could change the objects loaded in e, without having to wrap a reactive({}) scope around my shiny server code.
#Global Environment Pre-loaded before Shiny Server
e = new.env()
dataset1 = new.env()
load("dataset1.RData", env=dataset1)
dataset2 = new.env()
load("dataset2.RData", env=dataset2)
dataset3 = new.env()
load("dataset3.RData", env=dataset3)
ui = fluidPage(
# Application title
titlePanel(title="View Datasets"),
sidebarLayout(
# Sidebar panel
sidebarPanel(width=3, radioButtons(inputId = "datasetNumber", label = "From which dataset do you want to display sample data?", choices = list("Dataset1", "Dataset2", "Dataset3"), selected = "Dataset2")
),
# Main panel
mainPanel(width = 9,
textOutput("dataset"), # Bogus textOutput
textOutput("numInEnv")
)
)
)
loadEnvFn = function(input) {
if (input$datasetNumber=="Dataset1") {
.GlobalEnv$e = dataset1
} else if (input$datasetNumber=="Dataset2") {
.GlobalEnv$e = dataset2
} else {
.GlobalEnv$e = dataset3
}
# Bogus return string unrelated to real purpose of function loadEnvFn
return(input$datasetNumber)
}
server = function(input, output, session) {
output$dataset = renderText(sprintf("Dataset chosen was %s", loadEnvFn(input))) # Bogus output
output$numInEnv = renderText(sprintf("# objects in environment 'e': %d", length(ls(e))))
}
shinyApp(ui, server)
Goal
To upload a file in a Shiny app that reads the data, name the variables (columns) and do some data analysis before presenting plot output
Reference Shiny App
I am using this app from Shiny gallery as a reference: enter link description here
What I have tried:
I want to use the uploaded data in many outputs after doing different analyses. So, instead of reading file inside renderTable or renderPlot, I read it in server function:
server <- function(input, output) {
inFile <- reactive({input$file1})
sdf <- reactive({read.csv(inFile()$datapath, header=F)})
colnames(sdf()) <- c('Vehicle.ID', 'Time', 'Vehicle.class.no', 'Vehicle.type2',
'Vehicle.Length', 'Lane', 'Preceding.Vehicle.ID', 'Spacing','Spacing2', 'State',
'svel.mps', 'deltaV.mps', 'sacc', 'lane.change') }
Error
But when I run this app I get:
shiny::runApp('app-CC')
Listening on http://127.0.0.1:7484
Error in colnames(sdf()) <- c("Vehicle.ID", "Time", "Vehicle.class.no", :
invalid (NULL) left side of assignment
Question
How can I fix this error? I don't want to read the same file again in every render* function. Are there any online examples of shiny apps where a new file is read, column names are defined and then some analysis is done before using the render* functions?
You'd be better off assigning the column names during the read.csv
server <- function(input, output) {
inFile <- reactive({input$file1})
sdf <- reactive({
read.csv(inFile()$datapath, header=F, col.names = c('Vehicle.ID', 'Time', 'Vehicle.class.no', 'Vehicle.type2',
'Vehicle.Length', 'Lane', 'Preceding.Vehicle.ID', 'Spacing','Spacing2', 'State',
'svel.mps', 'deltaV.mps', 'sacc', 'lane.change')
)
})
}
Alternatively I believe you can perform multiple operations in the reactive block as long as you return the final object
server <- function(input, output) {
inFile <- reactive({input$file1})
sdf <- reactive({
dd<-read.csv(inFile()$datapath, header=F)
colnames(dd) <- c('Vehicle.ID', 'Time', 'Vehicle.class.no', 'Vehicle.type2',
'Vehicle.Length', 'Lane', 'Preceding.Vehicle.ID', 'Spacing','Spacing2', 'State',
'svel.mps', 'deltaV.mps', 'sacc', 'lane.change')
)
dd
})
}
An alternative is to use an actionButton and then to check for the validity of the files when you upload them. Lots of observeEvent is probably appropriate for firing off when something is "triggered," like a file being uploaded or a button being pressed. In addition, to make a chain of changes, it's probably best to have an "update" reactiveValue() thing to fire based on flags (which, I admit, is kind of messy, but works with Shiny since it doesn't have a proper callback framework like JS.)