reactive object in UI using Shiny R - r

I have developed an app with Shiny R which can make some plots changing inputs (selecting the variables of a data frame).
Until now, I had the data frame loaded in the workspace, let's call it "mydata".
So in my UI area I had things like:
selectInput('varX3', "Variable 3", choices = names(mydata))
and worked perfectly.
But now I have decided to implement another module in the app where I can import data from a CSV, so I import the data, I check if the data is correct and then click a button called "confirm" that creates a reactive object, mydata().
What's the problem now? Well, there are two main problems:
mydata doesn't exist anymore, so when compiling it fail.
if I change mydata for mydata() in the UI, it will make an error too since mydata() doesn't exist until I import the data and confirm the data imported is OK and also there's no supposed to be reactive objects in the UI (as far as I know).
I have come up with the solution of using renderUI and uiOutput, but I found tedious to change everything.
Is there another way of solving the problem?
To explain me better:
first: I import data from a CSV and check if the data is OK, click a button creating a reactive object, a dataframe with the data: mydata()
second: I make plots of the variables of this data frame, so the data frame appears in the UI necessarily.

Related

Manipulating a user-uploaded file/data in R shiny

I've been working on an R shiny app that is supposed to take a user-uploaded .csv file, do a bunch of manipulations/calculations to that data, and then spit out some graphs. I am completely new to shiny, however, and am very lost. I've tried to look this up, but most results I found take the data and directly show it in a graph. So far I've tried this (code is taken from here):
UI:
ui <- fluidPage(
fileInput("userfile", "Choose CSV File", multiple = TRUE,
accept=c("text/csv","text/comma-separated-values,text/plain",".csv")),
# etc.
Server:
server <- function(input,output){
df <- read.csv(input$userfile$datapath)
# some manipulation, such as adding a new column, for example:
df$timestwo <- 2 * df$column1
}
Is df in the Server code supposed to be the dataframe that was read in from the user-uploaded file? If not, how can I create a dataframe that is able to go through several changes, before making a graph? When I try to run my code I get "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." Thanks!
Edit to add: all the data manipulation I wanted to do worked with my sample dataset, but not the user-input one I tried out (which was the exact same data).

Using reactiveFileReader in Shiny app to update a dataframe upon change to underlying CSV

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.

ShinyApp reactive error while trying to select variables from imported data set

I am very new to Shiny and R in general and I am building an app that allows users to import data, select their variables, number of trees.. ect and then run that through a random forest script and have it display the outputs. Right now I am just working on the inputs, however, I am running into a problem. The user can import a CSV but then they cannot select their variables (headers from csv). I am trying to make it reactive so the user first must import their csv before the option of selecting their variables pops up (seems simple).
Here is my code right now:
ui.R
server.R
Error in Console
I am probably just making a silly mistake because I am unfamiliar with Shiny but your help would be much appreciated.
A little background info
First off, I recommend you read this through to get a good understanding of reactivity in Shiny: Shiny Reactivity Overview. This helps a lot with variable scoping in a Shiny context too: Scoping Rules in Shiny Apps.
The issue
I believe this issue is due to you defining the variable file_to_read within the scope of shiny output: output$input_file in your server.R file. When the function read.table() looks for the variable file_to_read, it doesn't exist since it is only defined within the scope of the shiny output.
Try making a reactive value and then assigning the input$file value to it once it is uploaded by the user. You'll also have to convert your dat1 variable to a shiny reactive since you can only read reactive values in the context of other reactive sources (e.g. observe(), reactive(), observeEvent(), etc.)
file_to_read <- reactiveVal(NULL) # Set the value to NULL to initialize
output$input_file <- renderTable({
if (is.null(input$file)) return () # Check there is an input file
file_to_read(input$file) # Set the reactiveVal to the input file
})
# Make dat1 a reactive so it can be read within a shiny reactive
dat1 <- reactive({
if(is.null(file_to_read()) return (NULL) # Check for input file in reactiveVal
read.table(file_to_read()$datapath, sep = input$sep, header = input$header)
})
# Make an eventReactive to capture when there is read.table() data from dat1
reactive1 <- eventReactive(dat1, {
if (is.null(dat1)) return ()
D <- colnames(dat1)
return (list(D, D[1]))
})
I didn't test this code since you posted your data in image format and I don't have an input file, but hope this helps your error.

Reading an RData file into Shiny Application

I am working on a shiny app that will read a few RData files in and show tables with the contents. These files are generated by scripts that eventually turns the data into a data frame. They are then saved using the save() function.
Within the shiny application I have three files:
ui.R, server.R, and global.R
I want the files to be read on an interval so they are updated when the files are updated, thus I am using:
reactiveFileReader()
I have followed a few of the instructions I have found online, but I keep getting an error "Error: missing value where TRUE/FALSE is needed". I have tried to simplify this so I am not using:
reactiveFileReader()
functionality and simply loading the file in the server.R (also tried in the global.R file). Again, the
load()
statement is reading in a data frame. I had this working at one point by loading in the file, then assigning the file to a variable and doing an "as.data.table", but that shouldn't matter, this should read in a data frame format just fine. I think this is a scoping issue, but I am not sure. Any help? My code is at:
http://pastebin.com/V01Uw0se
Thanks so much!
Here is a possible solution inspired by this post http://www.r-bloggers.com/safe-loading-of-rdata-files/. The Rdata file is loaded into a new environment which ensures that it will not have unexpected side effect (overwriting existing variables etc). When you click the button, a new random data frame will be generated and then saved to a file. The reactiveFileReader then read the file into a new environment. Lastly we access the first item in the new environment (assuming that the Rdata file contains only one variable which is a data frame) and print it to a table.
library(shiny)
# This function, borrowed from http://www.r-bloggers.com/safe-loading-of-rdata-files/, load the Rdata into a new environment to avoid side effects
LoadToEnvironment <- function(RData, env=new.env()) {
load(RData, env)
return(env)
}
ui <- shinyUI(fluidPage(
titlePanel("Example"),
sidebarLayout(
sidebarPanel(
actionButton("generate", "Click to generate an Rdata file")
),
mainPanel(
tableOutput("table")
)
)
))
server <- shinyServer(function(input, output, session) {
# Click the button to generate a new random data frame and write to file
observeEvent(input$generate, {
sample_dataframe <- data.frame(a=runif(10), b=rnorm(10))
save(sample_dataframe, file="test.Rdata")
rm(sample_dataframe)
})
output$table <- renderTable({
# Use a reactiveFileReader to read the file on change, and load the content into a new environment
env <- reactiveFileReader(1000, session, "test.Rdata", LoadToEnvironment)
# Access the first item in the new environment, assuming that the Rdata contains only 1 item which is a data frame
env()[[names(env())[1]]]
})
})
shinyApp(ui = ui, server = server)
Ok - I figured out how to do what I need to. For my first issue, I wanted the look and feel of 'renderDataTable', but I wanted to pull in a data frame (renderDataTable / dataTableOutput does not allow this, it must be in a table format). In order to do this, I found a handy usage of ReportingTools (from Bioconductor) and how they do it. This allows you to use a data frame directly and still have the HTML table with the sorts, search, pagination, etc.. The info can be found here:
https://bioconductor.org/packages/release/bioc/html/ReportingTools.html
Now, for my second issue - updating the data and table regularly without restarting the app. This turned out to be simple, it just took me some time to figure it out, being new to Shiny. One thing to point out, to keep this example simple, I used renderTable rather than the solution above with the ReportingTools package. I just wanted to keep this example simple. The first thing I did was wrap all of my server.R code (within the shinyServer() function) in an observe({}). Then I used invalidateLater() to tell it to refresh every 5 seconds. Here is the code:
## server.R ##
library(shiny)
library(shinydashboard)
library(DT)
shinyServer(function(input, output, session) {
observe({
invalidateLater(5000,session)
output$PRI1LastPeriodTable <- renderTable({
prioirtyOneIncidentsLastPeriod <- updateILP()
})
})
})
Now, original for the renderTable() portion, I was just calling the object name of the loaded .Rdata file, but I wanted it to be read each time, so I created a function in my global.R file (this could have been in server.R) to load the file. That code is here:
updateILP <- function() {
load(file = "W:/Projects/R/Scripts/ITPOD/itpod/data/prioirtyOneIncidentsLastPeriod.RData", envir = .GlobalEnv)
return(prioirtyOneIncidentsLastPeriod)
}
That's it, nothing else goes in the global.R file. Your ui.R would be however you have it setup, call tableOutout, dataTableOutput, or whatever your rendering method is in the UI. So, what happens is every 5 seconds the renderTable() code is read every 5 seconds, which in turns invokes the function that actually reads the file. I tested this by making changes to the data file, and the shiny app updated without any interaction from me. Works like a charm.
If this is inelegant or is not efficient, please let me know if it can be improved, this was the most straight-forward way I could figure this out. Thanks to everyone for the help and comments!

R shiny load data.frame for selectInput

I am trying to write a shiny app that loads several data frames. The data frames for my plots all work very well, but one data.frame which I want to use as a list of options in a dropdown menu does not load. If I load the frame seperately in the R session, everything works, but if I only run the shiny app, the selectors won't update.
library(shiny)
ui <- fluidPage(
#...
selectInput("mats", "Text",
selectors)
# ...
)
server <- function(input, output){
# ...
df1=read.csv("./data/file.csv", sep=";", head=T, stringsAsFactors = F)
df1$choices=as.character(df1$choices)
selectors=c("All", df1$choices)
#...
}
shinyApp(ui = ui, server = server)
I think, I need the selectors in the server function, so I loaded the data frame there together with my other data frames. Is that the right place and what do I need to do to get this running?
Best
There is a number of problems with your code:
Following the documentation, choices in selectInput should be a list.
If you want to create an element that would be available across the ui and server please consider reading the linked article on scoping rules in Shiny and defining your object in global.R.
With respect to the 1st point, if your intention is to use a data.frame column as a base for menu selection, you can apply the following transformation:
my_new_list <- split(df$id, df$subject)
as provided in this answer by #user3710546 to a similar question that I've asked in past.
Side note
Please consider having a look at the discussion on making a reproducible example in R. If you care to redo your example using some publicly available data, it will be easy to produce a solution. If I understood the problem correctly, you want to use data.frame column as a base for UI element, which is not difficult on its own.
.

Resources