Access dataframe in .Rdata from Shiny - r

I have a small shiny program below which reads in an .Rdata file using Shiny. The code below works as expected, but there is behavior that I desire that I cannot quite figure out how to implement from within Shiny.
To frame the first problem, suppose I have a file called tmp.Rdata and when that .Rdata is loaded into the R workspace it contains a data frame called "foo". My objective is to load the .Rdata using shiny and then access the data frame within it (foo) and do R things to it.
For example, if I was working within R proper, my actions would be something like:
> load('tmp.Rdata')
> str(foo)
And supposing foo is a data frame with K columns, the result of str(foo) would show the typical behavior of str() on the object.
When implementing the code below, the Shiny program runs and I can see the .Rdata loads successfully and that "foo" is in the workspace. However, the resulting action from str() in this instance is to show that the object foo is in the workspace, not the structure of the object foo itself, which is the behavior I want to implement.
chr "foo"
Now, if I know ahead of time that the .Rdata will always have in it a frame called "foo" I could hard code this into the server.R code and all is well. But, the more challenging problem is that my users will save an .Rdata file that contains within it a data frame with an unknown name.
I have searched SO and found similar questions, but not exactly tackling the same problem as far as I can tell, such as the one at the link below
Loading User input .Rdata on Shiny App
Thank you for any suggestions.
ui.R
shinyUI(fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(
h4("Uploading Files"),
fileInput('f1', 'Choose RData File', accept=c('.RData, .Rds'))
),
mainPanel(
h4("Data Structure"),
verbatimTextOutput("datastr")
)
)
))
server.R
shinyServer(function(input, output) {
dataInput <- reactive({
sessionEnvir <- sys.frame()
if (!is.null(input$f1)) load(input$f1$datapath, sessionEnvir)
})
output$datastr <- renderPrint({
ff <- dataInput()
if (is.null(dataInput())) return() else str(ff)
})
})

You could use eval() and parse(). The updated server.R code below shows how:
shinyServer(function(input, output) {
dataInput <- reactive({
sessionEnvir <- sys.frame()
if (!is.null(input$f1)) eval(parse(text = load(input$f1$datapath, sessionEnvir)))
})
output$datastr <- renderPrint({
ff <- dataInput()
if (is.null(dataInput())) return() else str(ff)
})
})
This will only work for RData files. Your ui.R appears to show a desire to accept Rds files too. You'll need to load them with readRDS. The good news is that readRDS will work just fine without eval() and parse(). Perhaps you could add radio buttons to the UI for the user to choose the file type.

Related

In Shiny, the submit button isn't running

I wrote a R script (MAIN.R) that converts PDF tables to CSV. When I run MAIN.R as an individual file, it functions well. I've tried it many times.
Currently, I'm working on a R shiny app that uses "MAIN.R" as a source and takes a pdf file as input. When I push the submit button, the output should appear in the MAIN panel. Unfortunately, the submit button does not function as intended.
May anyone please assist me with this, as I am new to Shiny?
UI.R
shinyUI(fluidPage(
titlePanel("DATASET CONVERSION"),
sidebarLayout(
fileInput("filein", label = h2("Select a file to convert.")),
submitButton("Submit")
),
mainPanel(
tableOutput("Dataset")
)
)
)
Server.R
source("MAIN.R")
shinyServer(function(input, output) {
outputdf <- reactive({ input$filein
})
output$Dataset <- renderTable({
outputdf()
})
})
Your Submit button is not currently linked to anything, so it will not do anything. If I am reading the code right, you are just taking the input dataset and storing it as the output of outputdf. Your output$Dataset then just picks up that outputdf and displays it as-is, without any work being done on it.
You use an action button like so:
## In UI.R
actionButton("execute", "Execute the Main Function")
## In Server.R
observeEvent(input$execute, {
## Do stuff here
})
Note that the actionButton has two parameters, inputID (which is how you refer to it) and text to display on top. For example, with input$filein, 'filein' is the inputID.
In Server.R, observeEvent won't do anything until it detects a change in input$execute, which happens when someone clicks the button. That is where you put your code to do stuff.
Now, in output$Dataset, you need to access the results of whatever you did in that observeEvent. One way to do that is to use a reactiveValue. This is just like a reactive, but instead of a function, it stores a data element. Initialize it as an empty dataframe, and then update it in the observeEvent. Something like this:
## In Server.R
treated_output <- reactiveValue(data.frame())
observeEvent(input$execute, {
## Run the function on the file
updated <- main_function(input$filein)
# Update your reactiveValue
treated_output(updated)
})
output$Dataset <- renderTable({
treated_output()
})
Does this make sense?

call R script from Shiny App

I developed a shiny app which displays some dynamic charts. These charts are generated at execution time according to the value of some buttons. This shiny app gets the data from a raw csv which is previously treated and transformed. I got a Rscript apart from the shiny app to do all those "transformations" of the raw data. What I would like to do is to call this Rscript from the shiny app in order to be executed when the shiny app is launched.
I have already checked these links but it didn't help at all: How can I connect R Script with Shiny app in R? and this one using Source() in Shiny. I checked the Rstudio documentation too: http://shiny.rstudio.com/tutorial/lesson5/.
I think it should be something like this, being procesadoDatos.R the RScript. i just want the source command to be executed at the beginning in order to load the data as the shiny app is starting:
source("procesadoDatos.R",local = TRUE)
shinyServer(function(input, output,session) {
(renderplots, reactives elements and so on)}
The Rscript is the shiny project path as the server.R and UI.R files. I also tried including the path but it didn't work either.
Another thing I tried was to create a function which makes all the transformations and then call it from server.R file after sourcing it:
source("procesadoDatos.R",local = TRUE)
generate_data(ticketsByService_report10.csv)
Being generate_data this function defined in the RScript:
generate_data <- function(csv_file) {
(all those transformation, data frame an so on)}
In all cases I got the same error saying that the data frames which are generated in the RScript aren't found.
Does anyone know what is wrong? Thanks in adavance
Scoping in Shiny
All this largely depends on where exactly you call source(). If you need the data to be found from within both the UI and the server function, you put source() outside the app.
If you place source() inside the server function, the UI won't be able to find any object created by the script. If you put this inside a render function, the objects are only visible inside that render function. See also Scoping rules for Shiny
Note that if you have separate server.R and ui.R files and you want the UI to find the objects created by the script, you should add a global.R file to your app directory. The source() command then goes in the global.R file.
A small example:
source('testScript.R')
shinyApp(
fluidPage(
selectInput("cols", "pick columns",
choices = names(x)),
dataTableOutput("what")),
function(input, output, session){
output$what <- renderDataTable(x)
}
)
and testScript.R contains one line:
x <- iris
The key here is:
the script actually has to create these objects
the script should be sourced in the correct spot.
So if you can do the following:
shinyApp(
fluidPage(
selectInput("cols", "pick columns",
choices = names(x)),
dataTableOutput("what")),
function(input, output, session){
source('testScript.R', local = TRUE)
output$what <- renderDataTable(x)
}
)
you get an error about not being able to find x. That's normal, because x is now only defined inside the environment of the server function.
You still can do this though:
shinyApp(
fluidPage(
dataTableOutput("what")),
function(input, output, session){
source('R/testScript.R', local = TRUE)
output$what <- renderDataTable(x)
}
)
Note how x is only needed inside the server function, not inside the UI.
Using functions
For functions, the same thing applies. You put the function definition in a script and source it like before. A function is nothing else but an object, so the script essentially creates a function object that then can be found using the exact same scoping rules.
Keep in mind though that this function should return something if you want to use the result of the function. So put this trivial example in testScript.R:
myfun <- function(x){
tmp <- iris[x]
return(tmp)
}
And now you can do the following:
source('testScript.R', local = TRUE)
shinyApp(
fluidPage(
selectInput("cols", "pick columns",
choices = names(myfun())),
dataTableOutput("what")),
function(input, output, session){
output$what <- renderDataTable(myfun(input$cols))
}
)
This doesn't work any longer if you put the source() inside the server function. The UI side won't be able to see myfun() any more.
rm(list = ls())
shinyApp(
fluidPage(
selectInput("cols", "pick columns",
choices = names(myfun())),
dataTableOutput("what")),
function(input, output, session){
source('R/testScript.R', local = TRUE)
output$what <- renderDataTable(myfun(input$cols))
}
)
# Error in myfun() : could not find function "myfun"

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!

fileInput: How to create a dataframe from a .xlsx file within shiny?

I am very new to shiny and ran quite fast in a problem which impedes to continue my work. I created a .xlsx file and tried to load this within shiny.
A similar problem was dicussed here but was not finally solved.
I tried to keep the code as simple as possible:
library(shiny)
ui <- fluidPage(
fileInput("uploadFile", "XLSX file"),
verbatimTextOutput("summary")
)
server <- function(input, output) ({
dataset<-reactive({
inFile <- input$uploadFile
dat<-read.xlsx("inFile$datapath", 1)
return(dat)
})
output$summary <- renderText({summary(dataset())})
})
Just loading the .xlsx file in R works fine with this code:
read.xlsx("testdata.xlsx", 1)
Adding browser() after inFile <- input$uploadFile in serverand calling inFile gives I think the correct object which contains .$datapath.
The error I get after uploading the file is:
Error in loadWorkbook(file) : Cannot find inFile$datapath
I hope this is not to silly but I just can figure out how to solve it. What I would like to know is how can I store my uploaded file as a dataframe within shiny?
You should leave the "" from the dat<-read.xlsx("inFile$datapath", 1) line.
This one works:
dat<-read.xlsx(inFile$datapath, 1)
Best,
RĂ³bert

reading excel files into Shiny

I'm new to Shiny and am trying to run an app using data from an excel file. Seems like it should be simple, but can't figure it out. there's load of info out there on more complex tasks (uploading files interactively, where you specify columns, file location etc) - but all I want is an app that uses data from a single excel file that has loaded in the background.
Similar questions have been asked before (Uploading csv file to shinyApps.io and R Shiny read csv file) but I didn't get a satistactory answer from them.
I have my excel file saved in the same directory as the app.R file, in a folder named 'data'. And I read it into the server part of my script like this:
server <- function(input, output){
# Read in data
myDF <- read_excel('data/MyShinyData.xlsx')
When I run the app.R file to test the app, it works fine. But when I publish it to the Shiny website using shinyapps::deployApp('pathToWorkingDirectory') I get a grayed out version of the app that has no interactivity. The app also publishes to the website fine if I simulate the data within the app.R file (the excel file is just this simulated data, written to excel with write.xlsx) - its only when I take out the code for simulating the data and replace it with the read_excel command that it stops working. I've also tried with a .csv file instead of .xlsx, but same problem.
I've copied the full code from the app.R file below.
What am I doing wrong? Thanks for your help.
library('ggplot2')
library('shiny')
library('psych')
library('readxl')
#===============
#This code makes a histogram, a coplot, and a prediction for species richness ('SpNat') given Forest cover ('NBT').
#===============
m1 <- lm(SpNat ~ NBT, data=myDF) #For prediction. best to create all non-reactive [ie non-updating] code outside the app, so it doesn't have to run every time.
#==========
# ui section
#==========
ui <- fluidPage(
### MAKING A TITLE
titlePanel("Dashboard based on excel data"),
### DIVIDING INPUTS TO SIDEBAR VS MAIN PANELS:
sidebarLayout(
sidebarPanel( #everything nested in here will go in sidebar
#dropdown input for coplot:
tags$h3('Select coplot variables'), #heading
selectInput(inputId='choiceX', label='Choose X variable',
choices=c('Species richness'='SpNat', 'Forest cover'='NBT', 'Pest control'='PC')), #***Choices are concatenated text strings.
selectInput(inputId='choiceY', label='Choose Y variable',
choices=c('Species richness'='SpNat', 'Forest cover'='NBT', 'Pest control'='PC')),
selectInput(inputId='choiceZ', label='Choose conditioning variable',
choices=c('Species richness'='SpNat', 'Forest cover'='NBT', 'Pest control'='PC')),
#checkbox input for pairs plots:
tags$h3('Select variables for pairs plots'), #heading
checkboxGroupInput(inputId='vars', label='Choose at least two variables for pairs plot',
selected=c('SpNat', 'NBT', 'PC'), #'determines which vars start off checked. Important for pairs, cos <2 and plot wont work.
choices=c('Species richness'='SpNat', 'Forest cover'='NBT', 'Pest control'='PC')), #***Server receives input as a single concatenated text
#slider input for prediction:
tags$h3('Predicting forest cover'), #heading
sliderInput(inputId='num',label='Pick a forest cover level', value=10, min=1, max=100)),
mainPanel( #everything nested in here will go in main panel
#specify output for app, including headings:
tags$h3('Coplot:'),
plotOutput(outputId='coplot'),
tags$h3('Histogram:'),
plotOutput(outputId='pairs'),
tags$h3('Predicted species richness:'),
verbatimTextOutput('prediction'))))
#==========
# server section
#==========
server <- function(input, output){
# Read in data
myDF <- read_excel('data/MyShinyData.xlsx') #don't need full path
myDF$PC <- as.factor(myDF$PC)
myDF <- select(myDF, SpNat, NBT, PC)
#create output object, and name it so it corresponds to the ui output function ID, plus use the ui input ID to create it:
output$coplot <- renderPlot(
ggplot(myDF, aes_string(input$choiceX, input$choiceY, col=input$choiceZ)) + geom_point()) #note use of aes_string to allow inputID use direct.
output$pairs <- renderPlot({
pairs.panels(subset(myDF, select=input$vars))})
output$prediction <- renderPrint({
newData <- data.frame(NBT=input$num)
cat(predict(m1, newdata = newData))
})
}
#==========
# and stitch together
#==========
shinyApp(ui=ui, server=server)
Figured it out. I had two problems:
(1) I had copied the app into a new folder prior to publishing, so the working directory had changed - needed to reset to the folder containing my app.R file prior to running shinyapps::deployApp.
(2) a couple of packages required by my app load automatically into my R console (I've made changes to my .Rprofile file). So while I didn't need to load these to run the app locally, I did to publish it online.
Both pretty dumb mistakes, but you live and learn.

Resources