I'm trying to return two tables based on one input. Basically, I'm reading in a CSV and I want to display the values in the CSV in a table, then in a second table display a count of the number of rows in the CSV.
Right now I have
ui.R
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
checkboxInput("header", "Header", TRUE)
),
mainPanel(
tableOutput("rawData"),
tableOutput("rawDataSize")
)
)
)
and server.R
server <- function(input, output) {
output$rawData <- renderTable({
inFile <- reactive({input$file1})
data <- reactive({
if (is.null(inFile))
return(NULL)
read.csv(inFile$datapath, header = input$header)
})
})
output$rawDataSize <- renderTable({
dim(data)[1]
})
}
Unfortunately, this returns Warning: Error in as.data.frame.default: cannot coerce class "c("reactiveExpr", "reactive")" to a data.frame
Move inFile <- reactive({input$file1}) outside of renderTable. Then, to use this reactive object, you need to treat it like a function. inFile()$datapath. Same for your data reactive object.
So, your server() ends up looking something like:
server <- function(input, output) {
inFile <- reactive({input$file1})
data <- reactive({
if (is.null(inFile())) return(NULL)
read.csv(inFile()$datapath, header = input$header)
})
output$rawData <- renderTable({
data()
})
output$rawDataSize <- renderTable({
dim(data())[1]
})
}
1- A reactive is a function, so always add () when calling it.
2- Avoid nesting reactives:
server <- function(input, output) {
inFile <- reactive(input$file1)
data <- reactive({
if (is.null(inFile()))
return(NULL)
read.csv(inFile()$datapath, header = input$header)
})
output$rawData <- renderTable(data())
output$rawDataSize <- renderTable(dim(data())[1])
}
Related
I have a Shiny app that includes a number of dropdown selection boxes, the values of which are filled from reading an RDS file. The app also includes a fileInput function to upload new data. How can I change the values in the dropdown boxes to reflect the new data? Currently I can see that the data is uploaded, but the old data remains in the dropdown.
The data that should be uploaded is saved to a file using
saveRDS( data.frame(names=c("Jill","Jane","Megan")),"myDataFrame.rds")
In my app.R file, I first define the 'default' value of the data:
myDataFrame <- data.frame(names=c("Tom","Dick","Harry"))
The content of my app.R is as follows:
library(shiny)
ui <- shinyUI(
fluidPage(
fileInput('file1', 'Choose file to upload',accept = ".rds"),
selectInput("myNames","Names",myDataFrame$names),
tableOutput('contents')
)
)
server <- shinyServer(function(input, output) {
output$contents <- renderTable({
inFile <- input$file1
if (is.null(inFile)) { return(myDataFrame) }
readRDS(inFile$datapath)
})
})
The initial view of the application is as expected: both the dropdown and the table contain the 'default' names. Upon upload of my RDS file containing a new dataframe, the table changes (which is what I was looking for) but the dropdown values do not. How can I make the latter happen?
I added reactive object myData that you have to use for table contents, but more importantly to update choices in selectInput (check observe and updateSelectInput part).
library(shiny)
ui <- shinyUI(
fluidPage(
fileInput("file1", "Choose file to upload", accept = ".rds"),
selectInput("myNames","Names", ""),
tableOutput("contents")
)
)
server <- function(input, output, session) {
myData <- reactive({
inFile <- input$file1
if (is.null(inFile)) {
d <- myDataFrame
} else {
d <- readRDS(inFile$datapath)
}
d
})
output$contents <- renderTable({
myData()
})
observe({
updateSelectInput(session, "myNames",
label = "myNames",
choices = myData()$names,
selected = myData()$names[1])
})
}
shinyApp(ui, server)
to riff off of #PoGibas' answer, I needed to load multiple list values for an app, here is a similar application using reactiveValues and observeEvent :
library(shiny)
# save a dummy RDS for loading
saveRDS(list(names=LETTERS,numbers=seq(10)),'dummy.rds')
# define initial values
myDataList <- list(names=c("Tom","Dick","Harry"), numbers=seq(5))
ui <- shinyUI(
fluidPage(
fileInput("file1", "Choose file to upload", accept = ".rds"),
selectInput("myNames","Names", ""),
selectInput("myNumbers","Numbers", ""),
tableOutput("contents")
)
)
server <- function(input, output, session) {
md <- reactiveValues(
names = myDataList$names,
numbers = myDataList$numbers
)
observeEvent(input$file1,{
d <- readRDS(input$file1$datapath)
for (n in names(d)){
md[[n]] <- d[[n]]
}
})
output$contents <- renderTable({
data.frame(data = c(md$names,md$numbers))
})
observe({
updateSelectInput(session, "myNames",
label = "myNames",
choices = md$names,
selected = md$names[1])
updateSelectInput(session, "myNumbers",
label = "myNumbers",
choices = md$numbers,
selected = md$numbers[1])
})
}
shinyApp(ui, server)
I have been referencing the following post which has been tremendously helpful in helping me understand Rshiny functionality:
How can I update plot from rhandsontable with uploaded data, without clicking into the table first?
I am still having some trouble grasping the concept of "Observe" so that may be the issue here. I want to upload 2 csvs where one is static (saving it in a separate tab) and one that can be edited (with edits reflected in the corresponding plot after hitting a button). My end goal is to be able to plot these data sets together on the same axes, but for now, I am having difficulty getting my table display which makes me think it's not uploading properly. I am using the following code:
library(shiny)
library(rhandsontable)
#sample data
year <- substr(Sys.Date(),1,4)
empty_dat=as.data.frame(matrix(1,nrow = 3,ncol = 4,dimnames = list(c("Cat A", "Cat B", "Cat C"),
c(paste("May",year),paste("June",year),paste("July",year),
paste("August",year)))))
ui = fluidPage(sidebarLayout(
sidebarPanel(
#static data input
fileInput('file1_new', 'Choose CSV File'),
#reactive data inut
fileInput('file1', 'Choose CSV File'),
#display reactive data
rHandsontableOutput('contents'),
actionButton("go", "Plot Update"),
width=7
),
mainPanel(
tabsetPanel(
#plot reactive data first tab
tabPanel("Plot", plotOutput("plot1")),
#table static data second tab
tabPanel("Table", tableOutput("table"))
)
)
))
server = function(input, output) {
#static input
output$table <- renderTable({
inFile <- input$file_new
if (is.null(inFile))
return(NULL)
read.csv(inFile$datapath, header = input$header,
sep = input$sep, quote = input$quote)
})
indat <- reactiveValues(data=empty_dat)
#reactive input
observe({
inFile = input$file1
if (is.null(inFile))
return(NULL)
data1 = read.csv(inFile$datapath)
indat$data <- data1
})
observe({
if(!is.null(input$contents))
indat$data <- hot_to_r(input$contents)
})
output$contents <- renderRHandsontable({
rhandsontable(indat$data)
})
#***example uses only one column-why I attempt multiple columns (indat$data[,1:4],indat$data[],indat$data) I get an error
#update data when user hits button
test <- eventReactive(input$go, {
return(indat$data[,3])
})
output$plot1 <- renderPlot({
#plot updated data
plot(test(),type = "l")
})
}
shinyApp(ui, server)
Any help would be greatly appreciated.
I made few changes to the code, and now the second tab displays the static table.
I am not sure if this is what you want but here's the code. The changes that I made are below the code.
library(shiny)
library(rhandsontable)
#sample data
year <- substr(Sys.Date(),1,4)
empty_dat=as.data.frame(matrix(1,nrow = 3,ncol = 4,dimnames = list(c("Cat A", "Cat B", "Cat C"),
c(paste("May",year),paste("June",year),paste("July",year),
paste("August",year)))))
ui = fluidPage(sidebarLayout(
sidebarPanel(
#static data input
fileInput('file1_new', 'Choose CSV File'),
#reactive data inut
fileInput('file1', 'Choose CSV File'),
#display reactive data
rHandsontableOutput('contents'),
actionButton("go", "Plot Update"),
width=7
),
mainPanel(
tabsetPanel(
#plot reactive data first tab
tabPanel("Plot", plotOutput("plot1")),
#table static data second tab
tabPanel("Table", tableOutput("table"))
)
)
))
server = function(input, output) {
#static input
output$table <- renderTable({
inFile <- input$file1_new
if (is.null(inFile))
{return(mtcars)}
else{
read.csv(inFile$datapath)}
})
indat <- reactiveValues(data=empty_dat)
#reactive input
observe({
inFile = input$file1
if (is.null(inFile))
return(NULL)
data1 = read.csv(inFile$datapath)
indat$data <- data1
})
observe({
if(!is.null(input$contents))
indat$data <- hot_to_r(input$contents)
})
output$contents <- renderRHandsontable({
rhandsontable(indat$data)
})
#***example uses only one column-why I attempt multiple columns (indat$data[,1:4],indat$data[],indat$data) I get an error
#update data when user hits button
test <- eventReactive(input$go, {
return(indat$data[,3])
})
output$plot1 <- renderPlot({
#plot updated data
plot(test(),type = "l")
})
}
shinyApp(ui, server)
So in the part where you do the renderTable, the variable name was slightly wrong :p, and in the read.csv, there were too many arguments.
output$table <- renderTable({
inFile <- input$file1_new
if (is.null(inFile))
{return(mtcars)}
else{
read.csv(inFile$datapath)}
})
Please let me know if this works for you... Cheers!
I need a shiny app to retrieve some file information as filename, size, numbers of rows and columns. My final ideia it is to build a box that automatically shows the metadata and if I import another dataset, it updates too. The original code I found here, but I tried to modify. Any help? I am new in Shiny R.
ui <- fluidPage(
titlePanel("Grabbing my file name"),
sidebarLayout(
sidebarPanel(
fileInput("file1", "Select your file",
multiple = FALSE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv",".xlsx")),
),
mainPanel(
textOutput("myFileName"),
textOutput("myFileSize"),
textOutput("myFileRow"),
textOutput("myFileCol"),
)
)
)
server <- function(input, output) {
file_name <- reactive({
inFile <- input$file1
if (is.null(inFile)){
return(NULL)
}else{
return (inFile$name)
}
})
output$myFileName <- renderText({ file_name() })
file_size <- reactive({
inFile <- input$file1
if (is.null(inFile)){
return(NULL)
}else{
return (file.size(inFile$name))
}
})
output$myFileSize <- renderText({ file_size() })
file_row <- reactive({
inFile <- input$file1
if (is.null(inFile)){
return(NULL)
}else{
return (nrow(inFile))
}
})
output$myFileRow <- renderText({ file_row() })
file_col <- reactive({
inFile <- input$file1
if (is.null(inFile)){
return(NULL)
}else{
return (ncol(inFile))
}
})
output$myFileCol <- renderText({ file_col() })
}
# Run the application
shinyApp(ui = ui, server = server)
We need to read the dataset to get the ncol/nrow. It would be more efficient to read the data once and get the ncol/nrow (here we used dim to get that instead of repeating ncol/nrow.
library(shiny)
-ui
ui <- fluidPage(
titlePanel("Grabbing my file name"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Select your file',
accept = c(
'text/csv',
'text/comma-separated-values',
'.csv'
)
)
),
mainPanel(
textOutput("myFileName"),
textOutput("myFileSize"),
textOutput("myFileColrow")
)
)
)
-server
server <- function(input, output) {
file_name <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL) else return (tools::file_path_sans_ext(inFile$name))
})
file_colrowsize <- reactive({
inFile <- input$file1
if (is.null(inFile)){
return(NULL)
}else{
tmp <- read.csv(inFile$datapath)
return (list(dim(tmp), object.size(tmp)))
}
})
output$myFileName <- renderText({ file_name() })
output$myFileColrow <- renderText({ file_colrowsize()[[1]] })
output$myFileSize <- renderText({file_colrowsize()[[2]]})
}
-Run the application
shinyApp(ui = ui, server = server)
Output from running
NOTE: We could also combine the two reactive into a single one and then return a list with 3 elements - 1) file name, 2) file size 3) dimensions
I know you said you want an R solution, but I think your options are pretty limited if you only consider this. How about an Excel-VBA solution. Go to the link below and download the sample file. That should do all you want, and a whole lot more.
http://learnexcelmacro.com/wp/2011/11/how-to-get-list-of-all-files-in-a-folder-and-sub-folders/
I use a widget of file uploading to accept input data.
In server.R:
getData = reactive({
inFile = input$file1
if (is.null(inFile)) return(NULL)
read.csv(inFile$datapath)
})
But sometimes the user may have no data, so I want to demo with my sample data. In server.R:
getData = eventReactive(input$Demo,{
read.csv('Sample.csv')
})
If I only put one option in the code, they each work. When putting them together, the eventReactive seems to overshadow reactive, and file loading isn't working. Somebody know what should I do about this? Thank you!
Complete code:
server.R
library(shiny)
source('1_Prepare.R')
source('Clean.R')
shinyServer(function(input, output) {
getData = reactive({
inFile = input$file1
if (is.null(inFile)) return(NULL)
read.csv(inFile$datapath)
})
getData = eventReactive(input$Demo,{
read.csv('TownShort.csv')
})
output$DataBefore = renderDataTable({
as.data.frame( getData() )
})
result = reactive({
if(!is.null(getData())) Clean(getData())
})
output$DataAfter = renderDataTable({
as.data.frame( result()[[1]] )
})
output$Aggregation = renderDataTable({
as.data.frame( result()[[2]] )
})
output$DataBad = renderDataTable({
as.data.frame( result()[[3]] )
})
})
ui.R:
library(shiny)
shinyUI(fluidPage(
titlePanel(h2("Data Cleaning: Town and State")),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose CSV File (two columns: Town and State)',
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')
),
tags$hr(),
actionButton('Demo', 'Demo with sample data')
),
mainPanel(
tabsetPanel(
tabPanel(title=h4('Data'),
column(5, tags$h3('Input Data'), dataTableOutput('DataBefore')),
column(5, tags$h3('After Clean'), dataTableOutput('DataAfter'))
),
tabPanel(title=h4('Aggregation'),
dataTableOutput('Aggregation')
),
tabPanel(title=h4('Unrecognized'),
dataTableOutput('DataBad')
)
)
)
)
))
UPDATE
I tested and found that, two input methods getData = reactive({...}) and getData = eventReactive(), whichever is behind in the code, it will overshadow the one in the front.
So I think, getData is a variable, I was defining it twice, and of course it forgets the first definitions. Here's fixed code:
server.R
library(shiny)
source('1_Prepare.R')
source('Clean.R')
DemoData = read.csv('TownShort.csv')
shinyServer(function(input, output) {
# one way to get data
getDemo = eventReactive(input$Demo, {DemoData
})
# the other way to get data
getUpload = reactive({
inFile = input$file1
if (is.null(inFile)) return(NULL)
read.csv(inFile$datapath)
})
# condition to choose data
getData = reactive({
if (!is.null(getUpload())) return(getUpload())
else if (!is.null(getDemo())) return(getDemo())
})
output$DataBefore = renderDataTable({
as.data.frame( getData() )
})
result = reactive({
if(!is.null(getData())) Clean(getData())
})
output$DataAfter = renderDataTable({
as.data.frame( result()[[1]] )
})
output$Aggregation = renderDataTable({
as.data.frame( result()[[2]] )
})
output$DataBad = renderDataTable({
as.data.frame( result()[[3]] )
})
})
There is still a problem: i can go back and forth between getData() and getDemo() only once. Specifically, I can use one of them, and switch to the other. And that's it. I can't change the value of getData() and more.
Is there a way to refresh the value of getData? Let me know please. Thanks!
I'm new to R-Shiny and my question might be very simple. After hours of thinking and searching, I couldn't solve the issue. Here is the problem:
1) My app asks user to upload his dataset.
2) Then in the server file, I read the dataset and I did some analyses and I report back the results into the user interface.
3)My user interface has 4 different out puts.
4) I read the dataset in the "render" function of each output. ISSUE: by doing so, the data is locally defined in the scope of each function which means that I need to read it over again for each output.
5) This is very in-efficient, Is there any alternative? using reactive ?
6) Below is a sample code showing how I wrote my server.R:
shinyServer(function(input, output) {
# Interactive UI's:
# %Completion
output$myPlot1 <- renderPlot({
inFile <- input$file
if (is.null(inFile)) return(NULL)
data <- read.csv(inFile$datapath, header = TRUE)
# I use the data and generate a plot here
})
output$myPlot2 <- renderPlot({
inFile <- input$file
if (is.null(inFile)) return(NULL)
data <- read.csv(inFile$datapath, header = TRUE)
# I use the data and generate a plot here
})
})
How can I just get the input data once and just use the data in my output functions ?
Thanks very much,
You can call the data from the file in a reactive function. It can then be accessed for example as
myData() in other reactive functions:
library(shiny)
write.csv(data.frame(a = 1:10, b = letters[1:10]), 'test.csv')
runApp(list(ui = fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv'))
),
mainPanel(
tableOutput('contents')
)
)
)
, server = function(input, output, session){
myData <- reactive({
inFile <- input$file1
if (is.null(inFile)) return(NULL)
data <- read.csv(inFile$datapath, header = TRUE)
data
})
output$contents <- renderTable({
myData()
})
}
)
)