reading excel files into Shiny - r

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.

Related

How can I make variables in a Shiny app acccessible to external R scripts deployed with the app?

I am relatively new to programming/R/Shiny. I have a network of R scripts that take in data, transform it, and produce figures that are output to a PDF. I have been using this for some time now. I now need to transfer that process to a Shiny app.
This has been problematic because none of the variables that are present in one script can be used in another.
Is there a "simple" way to make these variables accessible between R scripts ? That would be a much, much simpler solution than trying to transfer the entirety of the code into the app.r file. Any help would be much appreciated. Thanks.
Example code - Here, I would like the user to upload a CSV file and then press a button that runs an R script that modifies and adds to the data, creating two new dataframes. Those two dataframes would then be used in "report.R" to create PDF reports.
library(shiny)
ui <- fluidPage(
fileInput('upload', 'Choose file to upload',
accept ='.csv'
),
actionButton('analyze', 'Analyze the data')
)
server <- function(input, output, session) {
data <- reactive({
req(input$upload)
read.csv <- vroom(input$file$datapath)
})
observeEvent(input$analyze, {
source("analyze.R")
#this transforms data from the csv and produces new dataframes based on the original, call them df1 and df2. df1 and df2 are then used as data in reports generated by:
source("report.R")
}
)
}
shinyApp(ui, server)

Shiny App - passing the string to variable

I am trying to build Shiny App that does sentiment analysis. I have the code that works fine when i execute the script normally where Rstudio is importing the data from email.csv file. This file contains only 2 columns ( SentTo and RawText) and the text i am analyzing is located in B2 cell.
Once i run the code below i get nice chart that measure the sentiment.
library(readr)
library("ggplot2")
library('syuzhet')
Emails <- read_csv("C:/email.csv")
d<-get_nrc_sentiment(Emails$RawText)
td<-data.frame(t(d))
td_new <- data.frame(rowSums(td[1:14]))
names(td_new)[1] <- "count"
td_new <- cbind("sentiment" = rownames(td_new), td_new)
rownames(td_new) <- NULL
td_new2<-td_new[1:8,]
qplot(sentiment, data=td_new2, weight=count,
geom="bar",fill=sentiment)+ggtitle("Email sentiments")
Now what i am trying to do is to modify this code a bit and build the Shiny application by doing next:
ui.R
# Adding the Imput text field to the app
shinyUI(fluidPage(
textAreaInput("UserInput", "Caption", "Please Enter Your Text", width =
"500px", height = "300px"),
mainPanel(
plotOutput("distPlot"))
))
Server.R
library(shiny)
library(syuzhet)
library(ggplot2)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
Emails <- input$UserInput
d<-get_nrc_sentiment(Emails)
td<-data.frame(t(d))
td_new <- data.frame(rowSums(td[1:14]))
names(td_new)[1] <- "count"
td_new <- cbind("sentiment" = rownames(td_new), td_new)
rownames(td_new) <- NULL
td_new2<-td_new[1:8,]
qplot(sentiment, data=td_new2, weight=count,
geom="bar",fill=sentiment)+ggtitle("Email sentiments")
})
})
After i run the app i get this error:
So ti builds the app but even when i paste some text in to the field it seems that the code i am using in server.R part is not doing what it needs to do.
If i replace the line in server.R part (Emails <- input$UserInput) with (Emails <- read_csv("C:/email.csv"))
than it works fine. This tells me that the issue is within the way i am passing the text to the Emails. Through the input form its text through the csv file it is a second row and second column that contains the data. The code that follows i think its looking that specific format.
Anybody has suggestion on how to modify this to make it work?
Thank you in advance.
I believe the issue is in line:
td_new <- data.frame(rowSums(td[1:14]))
If I change it to the following, it works for me:
td_new <- data.frame(rowSums(td))
I'm not sure why you had the 1:14 index in there, but I don't see what it does.
Before plotting in shiny, first have it output the user input so that you understand what is being passed to the next step. verbatimTextOutput('input$UserInput') and verbatimTextOutput('dput(input$UserInput)'). I'm guessing this will be a character vector of length 1.
Now, go back to your code outside of shiny and pass it this same input, formatted exactly the same. Right now your code that works is getting a data.frame from a csv file and passed a column, which would be a character vector.
Once you get it working outside of shiny, using the input as parsed by shiny, the fixes to make your shiny app work should be clear.

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!

Allow user to load their data via CSV or use a sample dataset

I'm building a Shiny app that consists of
A fileInput for the user to upload a CSV of transactions
An actionButton that lets the user test the app with a pre-built dataset (i.e. without them having to load their own data).
A verbatimTextOutput that prints a preview of the dataset they're using and
Various plot and charts built using their selected dataset
If the user uploads a file, that dataset should become the "master" transactions dataset to feed the rest of the app. If they then click the "load sample data" button, that datset should turn into the "master" transactions dataset. (Extrapolate this idea to multiple alternations between them uploading data and clicking the button)
I can get this to work as follows:
# app.R
library(data.table)
library(shiny)
# UI
ui <- shinyUI(fluidPage(
fileInput(inputId='fi_file', label='Choose CSV File', accept=c('text/csv', 'text/comma-separated-values,text/plain', '.csv')),
actionButton(inputId="ab_loadSampleTransactions", label="Load Sample Transactions"),
verbatimTextOutput("vto_transactions")
))
# Server
server <- shinyServer(function(input, output) {
# When the user uploads a file, print it
observeEvent(input$fi_file, {
transactions <- read.csv(input$fi_file$datapath)
output$vto_transactions <- renderPrint(transactions)
})
# When the user clicks the button for sample transactions, print them
observeEvent(input$ab_loadSampleTransactions, {
transactions <- data.table(ID=c(1,2,3), Amount=c(100, 150, 125))
output$vto_transactions <- renderPrint(transactions)
})
# More logic involving the transactions dataset
# ...
})
# Run the application
shinyApp(ui = ui, server = server)
However, this is inefficient because it requires me to load the transactions dataset twice in order to display it and do future logic with it. I think I need to do something reactive here, but I can't figure out how since I have two separate methods for loading the data. Help?
Don't use global variables like the suggestion in the comment says.
Use reactiveValues. It sounds like you don't need this variable to be "global" in the sense that it needs to be shared with the UI and other files -- it just needs to be global within the server, correct? In that case, you can use reactiveValues and those variables can be accessed and set anywhere in your server

Rstudio Shiny - Updating a data.frame from Websource

I am trying to create a Shiny App.
I'd like it to download a CSV file from the web and store it on the local machine, then perform analysis.
My current approach is:
ui.R
library(shiny)
shinyUI(pageWithSidebar(
# Application title
headerPanel("TEST"),
sidebarPanel(
sliderInput("range", "Date Range:",
min = 0, max = 15, value = c(0,15))
),
# Show a tabset that includes a plot, summary, and table view
# of the generated distribution
mainPanel(
tabsetPanel(
tabPanel("Plot", plotOutput("plot"))
)
))
server.R
library(shiny)
shinyServer(function(input, output) {
datasetInput <- function(){
x1 <- strptime(Sys.time(), "%Y-%m-%d %H:%M:%S")
x2 <- strptime(file.info("/srv/shiny-server/Data/current.csv")$mtime, "%Y-%m-%d %H:%M:%S")
if ( difftime(x1, x2, units='mins') > 20 ){
str <- "wget http://www.web.com/file.csv -O /srv/shiny-server/Data/current.csv"
system(str)
}
data <- read.csv("/srv/shiny-server/Data/current.csv")
return(data)
}
output$plot <- renderPlot({
data <- datasetInput()
plot(data)
})
So, everything works. The data plots perfectly. The problem is the wget script doesn't get called. Regardless of where I put it.
For simplicity my main goal is to download and save a CSV file when the app runs. Then read in that CSV file as my main dataframe.
The ultimate goal is for my app to do the time check (Check if file is older then 20 minutes) every time someone does anything with the app. If it is older, I want to download/save the file, and update my data frame.
* note * using the wget function is a work around for a problem accessing a password protected CSV file.
The problem/solution is discussed here:
R Import - CSV file from password protected URL - in .BAT file
I don't know much about how Shiny works, the code used to generate the Shiny app is mostly from:
http://rstudio.github.io/shiny/tutorial/#tabsets
Try using the httr package instead of a raw wget. Here's an example of a Shiny application that also downloads a remote CSV file and parses it. https://github.com/trestletech/dallas-police/blob/master/shiny/server.R
Also you may find this tutorial valuable, as I think you could be using reactiveFunctions as the sources of your data input.

Resources