I'm drafting a simple Shiny App that provides access to a dynamic chart and a corresponding table. The relevant part of the server.R code looks like that:
output$some_plot<- renderPlot({
# Subset data on change in the indicator selection
chrt_demo_dta <- subset(x = dta_la_demo,
subset = <<my-conditions>>>)
# Define the demography chart
ggplot(data = chrt_demo_dta, aes(x = variable_a, y = variable_b)) +
geom_line(aes(colour = GEOGRAPHY_NAME), size = 2) +
theme_bw()}, height = 650, width = 800)
# Section generating table
output$chrt_demo_dta_tbl <- renderTable({chrt_demo_dta})
The problem occurs when I try to access the table I get the following error message:
Error in func() : object 'chrt_demo_dta' not found
It appears that the object chrt_demo_dta is created outside the scoping rules of the renderTable. My question is how can I achieve the following:
I want for the chart and the corresponding table to update dynamically upon the selection, hence my idea to embed the subset command in the renderPlot which works
I want to make use of the same subset in a corresponding table. Ideally, I would like to avoid repeating the subset command. As I have the required data frame ready it appears that it is only a matter of accessing it via the renderTable
I'm aware the the code is not fully reproducible but at this stage I'm not necessarily looking for a particular solution but a more generic guidance whether it would be possible to access an object created within one server element from other server element. If the push comes to shove, I could encapsule the subsetting mechanism in a function and call it twice but it seems to be rather messy solution.
In the server function of server.R:
# Create reactive object w/in server func
chrt_demo_dta <- reactiveVal()
output$some_plot<- renderPlot({
chrt_demo_dta.temp <- subset(
x = dta_la_demo,
subset = <<my-conditions>>>)
# Update the obj within scope of nested function
chrt_demo_dta(chrt_demo_dta.temp)
})
# Access of the updated obj is now possible in original scope
output$chrt_demo_dta_tbl <- renderTable({chrt_demo_dta()})
See related: Shiny : How to modify reactive object
The <<- operator may have unwanted consequences. It sends results to the shared environment, where it is visible to all users of the session. Not only will this create a race-condition where they try to over-write each other, it may expose confidential work to others. You have two solutions: 1) repeat the steps in each local environment, 2) write the result to disk with a unique name (e.g. Sys.time() + data hash). Then you can retrieve it when needed elsewhere. Don't forget to delete the saved file though, or your storage will be consumed.
Related
I am new to R shiny, and Stack Overflow in general. I am trying to create my first dashboard. I have an API call that goes out and collects data from a website called The Blue Alliance. The Blue Alliance is a website that hosts data for high school robotics competitions.
However, in order for the API call to know what event to pull its data from, it needs an event code, which is one of the inputs from a dropdown menu in my UI. How can I make the whole API call reactive so that it subs in the input from the dropdown as part of the URL for the API call?
I have tried placing the reactive() before the #Pull Event Scoring comment by doing something like
reactive(Scores_S4 <- input$Event_Name)
I then tried using it to wrap my entire function, both with and without {} on either side of the parentheses. I have found on various websites they both suggest to and to not use {} when using the reactive function.
server <- function(input, output){
reactive(
#Pull Event live scoring
Scores_S1 <- "https://www.thebluealliance.com/api/v3/event/2022",
Scores_S2 <- input$Event_Name,
Scores_S3 <- "?X-TBA-AUTH-KEY *API Key Goes Here*",
FScores <- paste0(Scores_S1, Scores_S2, Scores_S3),
EScores <- fromJSON(FScores),
EScores <- as.data.frame(EScores)
RPP1 <- ggplot(EScores, aes(reorder(`TeamName`, -`Avg. Hang`), `Avg. Hang`)) + geom_point()
output$RPPP1 <- renderPlot(RPP1))
}
I know that the API call is correct if the value of input$Event_Name is put in, I just need help understanding the reactive portion so that it uses current widget outputs while the app is running. In my understanding, if done correctly, this will allow my ggplot function to create a new graph depending on what is chosen for the input$event. I have a call to "plot output("RPPP1")" in my UI, so this should work once I understand the reactive piece.
Thank you for any and all time put into this, it is all helping a great deal to learn from a community this great.
Something like this might work:
EScores <- reactive({
#Pull Event live scoring
Scores_S1 <- "https://www.thebluealliance.com/api/v3/event/2022"
Scores_S2 <- input$Event_Name
Scores_S3 <- "?X-TBA-AUTH-KEY *API Key Goes Here*"
FScores <- paste0(Scores_S1, Scores_S2, Scores_S3)
EScores <- fromJSON(FScores)
as.data.frame(EScores)
})
output$RPP1 <- renderPlot({
ggplot(EScores(), aes(reorder(`TeamName`, -`Avg. Hang`), `Avg. Hang`)) + geom_point()
output$RPPP1 <- renderPlot(RPP1))
})
But, presumably you'll need something between
FScores <- paste0(Scores_S1, Scores_S2, Scores_S3),
and
EScores <- fromJSON(FScores),
that actually makes a call to the API, like curl() or GET().
I'm working on a complex (for me) Shiny app ~2500 lines of code. The basic structure is as follows, though I can't necessarily share a reproducible example since most of the information is confidential.
Right now, I am using df1 <- read.csv() etc to read in several CSV files as dataframes. I want to use reactiveFileReader() to make it such that the dataframes automatically update when the source CSV file is modified. I think my problem may be related to the fact that I am not doing this in a reactive context, but there is a reason for this. I am using the dataframes df1 etc to perform many calculations and to create new variables throughout the app (UI and server sections).
It also might be important to note that I am doing these file imports in the UI part of the Shiny app, since I need to rely on the factor levels of these dataframes to populate drop down selectInput in my UI. This might not be necessary.
Here is what I have tried (although I am pretty lost):
reader <- reactiveFileReader(intervalMillis = 1000, filePath =
"Data_Record.csv", readFunc = read.csv)
data_record <- reactive({
data_df <- reader()
return(data_df)
})
What I was expecting was for data_record to be a dataframe containing the information from the CSV, but it ends up being a "reactive expression". When I try to perform operations on data_record, like subsetting, I receive errors since that variable is not a dataframe.
Is there any way for me to update these dataframes upon modification to the underlying CSV outside of a reactive context? Even a scheduled update like every 10 seconds or so would work as well. My ultimate goal are dataframes that update when a CSV is modified, but scheduled updates are fine as well.
Thanks in advance for all the help and I apologize for not being able to supply a reproducible example! I will try to add more information as needed.
So if you want the data to be reactive, it has to be imported in the server section of the app as a 'reactive'. In shiny, 'reactives' become functions so to do anything with them you have to reference them their name followed by parenthesis inside a reactive function (reactive, observe, render etc).
For example, with your code above, reader becomes a reactive data frame. You can perform normal data manipulation on reader if you follow the rules of reactives outlined above.
# server.R
reader <- reactiveFileReader(intervalMillis = 1000, filePath =
"Data_Record.csv", readFunc = read.csv)
filtered_reader_df <- reactive({
reader() %>% filter(x > 100)
})
Where filtered_reader_df becomes a reactive filtered version of the reactive csv file. Again, to use filtered_reader_df in subsequent reactive function it must be referenced as filtered_reader_df() as it is a reactive function itself.
Finally, you can still use the reactive csv file to populate UI elements with the updateSelectInput() function inside an observer in the server. For example:
ui <- fluidPage(
selectInput("mySelectInput", "Select")
)
server <- function(input, output, session) {
reader <- reactiveFileReader(intervalMillis = 1000, filePath =
"Data_Record.csv", readFunc = read.csv)
observe({
select_input_choices <- unique(reader()$factor_column)
updateSelectInput(session, inputId = "mySelectInput", choices = select_input_choices)
})
}
The code above will update the choices of the select input every time the reader() data frame changes with the reactiveFileReader where unique(reader()$factor_column) are the reactive unique values of the factor column you want to populate the input with.
I'm working on an app in R where the users need to choose a file from their computer, with a RShiny fileInput button. I want to modify this, so that the associated variable can be assigned (i.e. a file can be loaded) automatically by the programm, without having the user click on the button and choose the file.
The problem I'm facing is that a fileInput has 4 fields, amongst which I only can know 3. For instance, when I load the file hello.csv in the variable inFile through the normal procedure, here is what I get :
inFile$name = hello.csv
inFile$size = 8320
inFile$type = text/csv
inFile$datapath = C:\\Users\\MyName\\AppData\\Local\\Temp\\Rtmpkh8Zcb/7d5f0ff0111d440c7a66b656/0
Though I could have guessed the second and the third one knowing the file, I have no idea how the datapath field is assigned...
I've tried to declare inFile as a NULL global variable, then to assign one by one the different fields, but I'm stuck with this last one. Is there an other way to do, like a function that mimics the behaviour of a user who clicks on the file input button and choose a specified file ?
Thank you very much.
If all you're looking to do is load a file initially, you don't have to rely on Shiny functions to do that. You can just rely on R functions. Set up your app like this:
ui <- shinyUI(
fileInput("inFile", label="Choose a file", multiple=F)
)
server <- shinyServer(function(input, output, session) {
values <- reactiveValues()
dat <- reactive({
if (is.null(inFile$datapath)) {
dat <- read.csv("path/to/your.csv")
values$file_name = "your.csv"
values$file_type = "csv"
values$file_size = file.size("path/to/your.csv")
values$file_path = "path/to/your.csv"
} else {
dat <- read.csv(inFile$datapath)
values$file_name = inFile$name
values$file_size = inFile$size
values$file_type = inFile$type
values$file_path = inFile$datapath
}
})
})
shinyApp(ui=ui, server=server)
In the above code, the Shiny app will start and see that inFile$datapath is NULL and will load a predefined file of your choosing. It won't run again until inFile changes, at which point it will load the file that the user pointed to.
Hope that helps.
Update
I changed the code above to use reactiveValues to store the pieces of information that need to be used throughout the app. If you just set those and then do a find/replace for input$inFile$datapath and replace it values$file_path, your code should work just fine.
Here is how I figured it out :
I edited the original code, so that all the read.csv(...) are replaced with calls to a data.frame global variable. I also added a small button that you need to click on before you continue. This button saves what you just loaded in the Database (if you chose a file with the fileInput) and assigns the right values to the global variables that will be needed for the following operations. If you chose no file at all, it will directly assign the variables from the data found in the Database.
So I did not find a proper solution to the problem, but this is a workaround that will do the job in my case.
#brittenb I couldn't get your reactive solution to work as I wanted to, that's why I ended up doing this another way. Thanks for having taken the time to think about it though.
I'm still open to suggestions on how to update the file in a fileInput without user interaction.
Currently I have a function [degtest] which is created in the shiny server, that returns a list,
return(list(datatable=datatable, predicttable=predicttable, esttable=esttable)
I want this list to be accessible after the function has run so that I can use different parts of the list to be rendered separately.
outlist <- reactive({
if(is.null(input$file2)){return(NULL)}
if(input$d2 == 0){return(NULL)}
with(data = reactdata$degdata, degtest(reactdata$degdata[,input$selectTemp], reactdata$degdata[,input$selectPot],reactdata$degdata[,input$selectWeight], reactdata$degdata[,input$selectTime], input$Temp0))
})
input$file2 is my reactdata (reactdata$degdata and input$d2 is an action button.
I thought i'd be able to reference outlist$datatable but R says ' object of type 'closure' is not subsettable'
When you are making an object reactive, you are actually making it into a sort of function (closure), so you have to use it as outlist() rather than outlist. See this similar question. It's hard to answer your question considering you did not provide a reproducible example, but I think your solution will be something like outlist()$ObjectYouAreTryingToAccess.
Any ideas on how to update a data frame that shiny is using without stopping and restarting the application?
I tried putting a load(file = "my_data_frame.RData", envir = .GlobalEnv) inside a reactive function but so far no luck. The data frame isn't updated until after the app is stopped.
If you just update regular variables (in the global environment, or otherwise) Shiny doesn't know to react to them. You need to use a reactiveValues object to store your variables instead. You create one using reactiveValues() and it works much like an environment or list--you can store objects by name in it. You can use either $foo or [['foo']] syntax for accessing values.
Once a reactive function reads a value from a reactiveValues object, if that value is overwritten by a different value in the future then the reactive function will know it needs to re-execute.
Here's an example (made more complicated by the fact that you are using load instead of something that returns a single value, like read.table):
values <- reactiveValues()
updateData <- function() {
vars <- load(file = "my_data_frame.RData", envir = .GlobalEnv)
for (var in vars)
values[[var]] <- get(var, .GlobalEnv)
}
updateData() # also call updateData() whenever you want to reload the data
output$foo <- reactivePlot(function() {
# Assuming the .RData file contains a variable named mydata
plot(values$mydata)
}
We should have better documentation on this stuff pretty soon. Thanks for bearing with us in the meantime.