shiny - reactive() to select between two reactive() - r

I'm making an app in Shiny which allows users to upload data or upload a save file.
If a user uploads new data then the flow looks like:
Upload Files >> Select Sheets >> Select Columns >> Sentence Tokenization >> Word Tokenization >> Graphics
Originally, I had been using observe() statements with reactiveValues() so at each step, I stored off the results into a reactiveValues(). This enabled the save file upload to work like:
Upload Save File >> Set sentences >> Set words >> Graphics
I would like to be able to accomplish something similar but using reactive() statements. So, given that I have uploadedFiles = reactive() and uploadedSave = reactive() how do I write a reactive with the following pseudo code:
rawText = reactive({
if uploadedFiles() flushes reactivity then uploadedFiles()
else if uploadedSave() flushes reactivity then uploadedSave()
else NULL
})
I don't have an "error" I'm trying to troubleshoot, just trying to think through the process of using a reactive to act like a 'most recently flushed' gate to allow my data flow to start from two different places and converge to a single one.

I'm going to show how I do it, but I have a feeling it is not a great method. I'm interested in hearing what the best method is and what's wrong with mine.
new_react <- reactiveValues()
new_react$FilesReact <- 0
new_react$SaveReact <- 0
invalidate <- function(x) x <- x+1
uploadedSave <- eventReactive(new_react$SaveReact,{
# Set sentences >> Set words >> Graphics
})
uploadedFiles <- eventReactive(new_react$FilesReact,{
# Select Sheets >> Select Columns >> Sentence Tokenization >> Word Tokenization >> Graphics
})
## I don't know how you are going to determine whether it's a data file
# or a save file...
if (its_a_save_file) {
new_react$SaveReact <- invalidate(new_react$SaveReact)
uploadedSave()
}else if (its_a_data_file) {
new_react$FilesReact <- invalidate(new_react$FilesReact)
uploadedFiles()
So basically I just define two reactive values which when invalidated by that simple function, will allow for the reactive functions, uploadedSave() or uploadedFiles() to be called.
Edit: To me, this just looks like a way to force eventReactive() to work like observeEvent()

Without more code, it is hard to be specific. Here is an app. that displays a different value based on what is the last control used. The controls are monitored using observeEvent() and each will put its input into a reactive value. Then the output just works with whatever is in the reactive value. For your case you need to have an observeEvent() for each of the controls you are using to do file and save uploads. Then they each put their file into a common reactiveValue and that value is used for further processing.
library(shiny)
app<-shinyApp(
ui=(fluidPage(sliderInput("First", min=1, max=10, label="Slider", value=1),
actionButton("Second", label="Second Button"),
textOutput("OutText"))
),
server=function(input,output){
RV<-reactiveValues(data=NULL)
observeEvent(input$First,
RV$Text<-input$First)
observeEvent(input$Second,
RV$Text<-"Second")
output$OutText<-renderText({
req(RV$Text)
RV$Text
})
}
)
runApp(app)

Related

Perform multiple actions upon pressing an actionButton

I've been struggling to get my Shiny app to perform multiple operations upon pressing a single actionButton. I've looked around a lot and haven't been able to solve it. I've tried eventReactive, observeEvent, putting everything in one block, separating them out - you can see the eventReactive and observeEvent attempts in the server.R code.
I have a button which is supposed to run some models, once pressed. Upon pressing the button, I'd like to do quite a few things:
Print a message letting the user know that something is happening
Concatenate the previously-defined user options into a .sh file and execute model prediction
Periodically check for the model output file
Once output file has been created, print "Done" and save as a global variable for further analysis
This is what I have so far (it doesn't work, the model runs [yay!] and that's it). It's particularly confusing to me that the sys command runs fine, but the "Running" message doesn't get printed. I've made sure that I haven't misspelt any var names.
Dummy python script to produce an output: Let's call it script_shiny.py
import time
import pandas as pd
# construct dummy df
d = {'col1': [1, 2], 'col2': [3, 4], 'col3': [5,6]}
df = pd.DataFrame(data=d)
time.sleep(60) # let's add a delay to simulate the 'real' situation
df.to_csv("test_out.txt",sep="\t") # create the output file to be detected
The relevant part of ui.R
*CODE TO CAPTURE PARAMS, WORKS FINE*
shiny::actionButton("button", "Run PIDGIN"), # RUN MODELS
tags$br(),
textOutput("pidginrunning"), # OUTPUT RUNNING MESSAGE
textOutput("pidgindone"), # OUTPUT DONE MESSAGE
The relevant part of server.R
# One approach - observeEvent and defining renderText and .sh file creation and running all together
observeEvent(input$button, { # OBSERVE BUTTON BEING PRESSED
output$pidginrunning <- renderText({ # LET USER KNOW ITS RUNNING - NOT WORKING
paste0("Running PIDGIN...")
})
# CREATE .SH FILE AND RUN DUMMY SCRIPT - WORKING (params previously defined)
bin_bash <- "#!/bin/bash"
output_name <<- "test_out.txt"
runline <- paste0("python script_shiny.py")
bash_file <- data.frame(c(bin_bash,runline))
write.table(bash_file,"./run_pidgin.sh",quote=F,row.names=F,col.names=F)
system("bash -i run_pidgin.sh")
})
# CHECK OUTPUT - NOT WORKING
# Defining output_name in the previous code block works, its defined as a global variable
# Other approach, use eventReactive to define a 'function' which is called separately
checkOutput <- eventReactive(input$button, {
reactivePoll(1000, session, checkFunc = function() {
if (file.exists(output_name)) # check for the output file
assign(x = "preds", # Read it in if so, and define global variable
value = read.csv(output_name,header=T,sep="\t"),
envir = .GlobalEnv)
paste0("Done. Please move onto the Results tab.")
})
})
output$pidgindone <- renderText({ # PRINT DONE MESSAGE SO USER KNOWS TO MOVE ON
checkOutput()
})
(Hope that counts as minimum example. As a proxy I suppose you can generate a dummy .sh file which produces a .txt file table, instead of running the models).
Cheers for your time and help.
EDIT: added example script which creates an output which should be detected

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!

How to update the file related to a `fileInput` variable in R Shiny without user interaction?

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.

Shiny App : getting input choices from an uploaded file

I'm trying to use Shiny to get data from a file and use this data to populate a list.
The process is pretty standard. On a server.R file, I get a vector called 'list' by reading a csv file. I want to be able to select the value from this vector with a selectInput() function. So I put an observe loop to check if the file is uploded, and if it's correct, list is created.
observe({
inFile<-input$DATA
if(!is.null(inFile)){
data = read.table(inFile$datapath, header=T,sep=";")
list=unique(data$SLE)
}else{
list=NULL
}
})
My selectInput() is like this :
output$SLE = renderUI({ selectInput('SLE1', label='Choose the item :', levels(list)) })
If I put the selectinput() in the observe loop, the select box works well, except that every time the observe loop is executed, the selectbox is reset. So, it's not a solution.
If I leave the select box out of the observe loop, list keeps the defaut value even if the data file is loaded.
I've tried to set list as a reactive value but it wasn't a success. How can I set list?
You can have the select input inside a conditional panel in UI.R itself and
use a function in place of "choices=" to retrieve the data frame as the list of choices.
One example is as below:
in ui.R:
selectInput("id", "Select Employee:", get.Employee())
Required funcion:
get.Customers <- function(input){
# YOU CAN ENTER YOUR CODE IN HERE
df_input <- input$nr
return(df_input)
}

Resources