I'm trying to create a shiny app that uploads a table, runs a function, and displays a graph and table. The uploading file works fine, but I am unable to run the function and output the graph and table (We shall only focus on the table for now). I am presented with the error:
Warning: Error in read.table: 'file' must be a character string or connection
I have run the function separately in R, and works fine with the desired output. I have tried different read functions, and different separators/delimiters, and read the function in the reactive renderPlot function (as described in a previous post here). Below is a snippet of the code I've been working on:
ui.R:
fileInput("file1",
"Load Input File",
accept = c("text/csv", "text/comma-separated-values,text/plain",".csv")
)
server.R:
execute = observeEvent(input$runButton, {
output$plot1 = renderPlot({
inFile = input$file1
if (is.null(inFile)) {
print(NULL)
}
podr_fun_graphs(inFile$datapath)
})
}
podr_graphs function:
podr_fun_graphs <- function(p) {
df1 <- read.delim(p, sep = "\t")
... # From here there is some data cleaning and manipulation of df1
}
Code similar to this was working a few weeks ago, I made some small changes and it then broke. Would appreciate any help to fix this.
Thanks
the problem is in your if statement. You have written print(NULL). But it should be:
if (is.null(inFile)) {
return(NULL)
}
R will go on to execute podr_fun_graphs(inFile$datapath) if you don't specify return.
Related
What is the best way to only render a data table in shiny if data exists? Right now, I am getting the following error because I am telling shiny to render a data table, even when it is NULL.
Warning in file(file, "rt") :
cannot open file '\': No such file or directory
Warning: Error in file: cannot open the connection
My code is split like this, where I read the data once the user chooses a csv file. After a user chooses a csv file, the error goes away and everything works fine. How do I tell Shiny to not display a data table until a valid file is chosen?
filedata <- reactive({
if (is.null(input$file_selector)){
# User has not uploaded a file yet
return(NULL)
} else {
read.csv(paste0(parseDirPath(c(home = 'C:\\Users\\Ruben'), file_dir()),'\\',input$file_selector),skip=1)}
})
output$filetable <- renderDataTable({
filedata()
})
I've tried putting the output$filetable <- .... code after the read.csv... line in the filedata <- ... function, but that doesn't work either. What else should I be doing here?
You can use req() function, this will check whether the file the file is present or not then it will go to the proceding code
filedata <- reactive({
file <-input$file_selector
req(file)
read.csv(paste0(parseDirPath(c(home = 'C:\\Users\\Ruben'), file_dir()),'\\',file),skip=1)}
})
Please use req(), as shown below
filedata <- reactive({
req(input$file_selector)
read.csv(paste0(parseDirPath(c(home = 'C:\\Users\\Ruben'), file_dir()),'\\',input$file_selector),skip=1)
})
I have the shiny app below which displays a plotly() oblect and a download button to download it. While png and jpeg are downloaded normally there seems to be an issue when I try to download the svg file.
library(shiny)
library(plotly)
library(webshot)
ui <- fluidPage(
plotlyOutput("plot"),
uiOutput("down")
)
server <- function(input, output) {
# renderPlotly() also understands ggplot2 objects!
save<-reactive({
plot_ly(mtcars, x = ~mpg, y = ~wt)
})
output$plot <- renderPlotly({
save()
})
output$down<-renderUI({
#Download files with quotes or not depending on the quote=input$quotes which has value TRUE or FALSE.
output$downloadData <- downloadHandler(
filename = function() {
paste("test", "svg", sep = ".")
},
# This function should write data to a file given to it by
# the argument 'file'.
content = function(file) {
# Write to a file specified by the 'file' argument
export(save(), file=file)
}
)
downloadButton("downloadData", "Download",class = "butt1")
})
}
shinyApp(ui, server)
I'm not sure why you put the switch in there in the first place, but it looks very out of place to me.
As far as I know, switch is not a plotly- or shiny-function, but just the base-R function. And that function just helps you choose between cases, as a kind of help to prevent you from having to write endless if-else if-else if-...-statements.
It chooses from the 2nd to n-th argument based on the first, something like this:
myvar <- sample(c('one', 'two', 'three'))
counted <- switch(myvar,
one='First',
two='Second',
three='Third',
'Error')
The unnamed argument is there as a fallback, default value. Syntactically it's not wrong to give a default as the only value, but it does defeat the value. Basically, what you've written is sep <- "jpeg", and then moved on, not using sep. You can check it by changing "jpeg" to something else, there is no difference.
In the next step, you're just trying to save your file, which is named correctly.
That causes export to try to save the plot in the format as recognised by the extension. Which can be done automatically for jpegs and pngs, but not for svg.
Some help is written in ?plotly::export, where it tells you it needs something from the RSelenium-package. Which unfortunately I can't help you with, but I think you can find answers by googling more of that package.
But the switch-line is misleading you, it's at least not working the way you are trying to use it.
I am using Shiny as an interface for viewing tables stored locally in a series of .RData files however I am unable to get the table to render.
My server code is like this:
output$table1 <- renderTable({
load(paste0(input$one,"/",input$two,".RData"))
myData})
On the ui side I am simply displaying the table in the main panel.
This other SO question suggests that the issue is that the environment that the data is loaded into goes away so the data isn't there to display. They suggest creating a global file and loading the .RData file in there, but I don't believe I will be able to load the data dynamically that way. Any guidance on how to use .RData files effectively within shiny would be appreciated.
Regards
I think you just need to move the load statement outside of the renderTable function. So you should have
load(paste0(input$one,"/",input$two,".RData"))
output$table1 <- renderTable({myData})
If you look at the help file for renderTable, the first argument is
expr: An expression that returns an R object that can be used with
xtable.
load does not return this.
I got around this by "tricking" R Shiny. I make a BOGUS textOutput, and in renderText, call a external function that, based in the input selected, sets the already globally loaded environments to a single environment called "e". Note, you MUST manually load all RDatas into environments in global.R first, with this approach. Assuming your data isn't that large, or that you don't have a million RDatas, this seems like a reasonable hack.
By essentially creating a loadEnvFn() like the below that returns a string input passed as input$datasetNumber, you can avoid the scoping issues that occur when you put code in a reactive({}) context. I tried to do a TON of things, but they all required reactive contexts. This way, I could change the objects loaded in e, without having to wrap a reactive({}) scope around my shiny server code.
#Global Environment Pre-loaded before Shiny Server
e = new.env()
dataset1 = new.env()
load("dataset1.RData", env=dataset1)
dataset2 = new.env()
load("dataset2.RData", env=dataset2)
dataset3 = new.env()
load("dataset3.RData", env=dataset3)
ui = fluidPage(
# Application title
titlePanel(title="View Datasets"),
sidebarLayout(
# Sidebar panel
sidebarPanel(width=3, radioButtons(inputId = "datasetNumber", label = "From which dataset do you want to display sample data?", choices = list("Dataset1", "Dataset2", "Dataset3"), selected = "Dataset2")
),
# Main panel
mainPanel(width = 9,
textOutput("dataset"), # Bogus textOutput
textOutput("numInEnv")
)
)
)
loadEnvFn = function(input) {
if (input$datasetNumber=="Dataset1") {
.GlobalEnv$e = dataset1
} else if (input$datasetNumber=="Dataset2") {
.GlobalEnv$e = dataset2
} else {
.GlobalEnv$e = dataset3
}
# Bogus return string unrelated to real purpose of function loadEnvFn
return(input$datasetNumber)
}
server = function(input, output, session) {
output$dataset = renderText(sprintf("Dataset chosen was %s", loadEnvFn(input))) # Bogus output
output$numInEnv = renderText(sprintf("# objects in environment 'e': %d", length(ls(e))))
}
shinyApp(ui, server)
I am following the simple file upload example from shiny gallery, but with a slight modification. I am required to modify the csv files locally, and see the changes reflected on the UI. However, I believe that this is not possible unless we poll for any changes in the source.
Therefore, I simplify the problem, by allowing re-uploading of the file. But, this is also not happening in Shiny. Once a file "file1.csv" is uploaded, i cannot upload the same file again. I have to upload a different file "file2.csv" and then once again the original file "file1.csv".
This is just time consuming, and I am wondering if anyone has come across such an issue, and possibly found a solution for it.
Adding a jquery to clear the value of the fileInput does the trick.
...
fileInput("csvInput", "Upload CSV file", multiple = TRUE,
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')),
tags$script('$( "#csvInput" ).on( "click", function() { this.value = null; });'),
...
Actually it is one of the shortcomings of Shiny's fileInput module: it stays still if the same file was selected again, and there is no force-upload option built-in.
To enforce re-uploading, the basic idea is:
After each time uploading, keep the uploaded data in a reactiveValues as storage.
Apply a new fileInput to replace original one
Show a successfully upload message under new fileInput for a better user experience.
In ui.R,using
uiOutput('fileImport')
In server.R:
# reactive storage
v <- reactiveValues()
# fileInput index
v$fileInputIndex <- 1
updateFileInput <- function (name = NULL){
# update output with a new fileInput module
output$fileImport <- renderUI({
index <- isolate(v$fileInputIndex)
result <- div()
result <- tagAppendChild(
result,
fileInput(paste0('file', index), 'Choose CSV File',accept=c('text/csv','text/comma-separated-values,text/plain','.csv'))
)
# show a message of successful uploading
if(!is.null(name)){
result <- tagAppendChild(
result,
div(name," upload complete")
)
}
result
})
}
dataInputRaw <- reactive({
# equals to `input$file1` when initialized
inFile <- input[[paste0('file', v$fileInputIndex)]]
# TICKY PART:
# 1. If initialized, `inFile` and `v$data` are both `NULL`
# 2. After each uploading, new `fileInput` is applied and
# we want to keep previous updated data.
# It also prevent recursive creation of new `fileInput`s.
if (is.null(inFile)){
return(v$data)
}
# load as data frame
v$data <- data.frame(read.csv(inFile$datapath))
# if file successfuly uploaded, increate the index
# then upload `fileInput` with successful message
if (!is.null(v$data)){
v$fileInputIndex <- v$fileInputIndex + 1
updateFileInput(name = inFile$name)
}
# return data
v$data
})
# init
updateFileInput()
I have tested this snippet and it works.
Goal
To upload a file in a Shiny app that reads the data, name the variables (columns) and do some data analysis before presenting plot output
Reference Shiny App
I am using this app from Shiny gallery as a reference: enter link description here
What I have tried:
I want to use the uploaded data in many outputs after doing different analyses. So, instead of reading file inside renderTable or renderPlot, I read it in server function:
server <- function(input, output) {
inFile <- reactive({input$file1})
sdf <- reactive({read.csv(inFile()$datapath, header=F)})
colnames(sdf()) <- c('Vehicle.ID', 'Time', 'Vehicle.class.no', 'Vehicle.type2',
'Vehicle.Length', 'Lane', 'Preceding.Vehicle.ID', 'Spacing','Spacing2', 'State',
'svel.mps', 'deltaV.mps', 'sacc', 'lane.change') }
Error
But when I run this app I get:
shiny::runApp('app-CC')
Listening on http://127.0.0.1:7484
Error in colnames(sdf()) <- c("Vehicle.ID", "Time", "Vehicle.class.no", :
invalid (NULL) left side of assignment
Question
How can I fix this error? I don't want to read the same file again in every render* function. Are there any online examples of shiny apps where a new file is read, column names are defined and then some analysis is done before using the render* functions?
You'd be better off assigning the column names during the read.csv
server <- function(input, output) {
inFile <- reactive({input$file1})
sdf <- reactive({
read.csv(inFile()$datapath, header=F, col.names = c('Vehicle.ID', 'Time', 'Vehicle.class.no', 'Vehicle.type2',
'Vehicle.Length', 'Lane', 'Preceding.Vehicle.ID', 'Spacing','Spacing2', 'State',
'svel.mps', 'deltaV.mps', 'sacc', 'lane.change')
)
})
}
Alternatively I believe you can perform multiple operations in the reactive block as long as you return the final object
server <- function(input, output) {
inFile <- reactive({input$file1})
sdf <- reactive({
dd<-read.csv(inFile()$datapath, header=F)
colnames(dd) <- c('Vehicle.ID', 'Time', 'Vehicle.class.no', 'Vehicle.type2',
'Vehicle.Length', 'Lane', 'Preceding.Vehicle.ID', 'Spacing','Spacing2', 'State',
'svel.mps', 'deltaV.mps', 'sacc', 'lane.change')
)
dd
})
}
An alternative is to use an actionButton and then to check for the validity of the files when you upload them. Lots of observeEvent is probably appropriate for firing off when something is "triggered," like a file being uploaded or a button being pressed. In addition, to make a chain of changes, it's probably best to have an "update" reactiveValue() thing to fire based on flags (which, I admit, is kind of messy, but works with Shiny since it doesn't have a proper callback framework like JS.)