I am building a shinydashboard that I want to allow a user to be able to load an excel file and then generate plots and additional output items. The problem I am running into is I can load the data:
ui <- dashboardPage(
dashboardBody(
tabItems(
tabItem(tabName = "data_file",
fluidPage(
titlePanel("Upload_Data_File"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose xlsx file',
multiple = TRUE,
accept = c(".xlsx"))
),
mainPanel(
tableOutput('contents'))
),
DT::dataTableOutput("sample_table"))
))))
server <- function(input, output) {
df_products_upload <- reactive({
inFile <- input$file1
if(is.null(inFile))
return(NULL)
file.rename(inFile$datapath,
paste(inFile$datapath, ".xlsx", sep=""))
read_excel(paste(inFile$datapath, ".xlsx", sep=""), 1)
})
output$sample_table <- DT::renderDataTable({
df <- df_products_upload()
DT::datatable(df)
})
shinyApp(ui = ui, server = server)
If I'd like to do some analysis on this dataframe I can do this inside of the server option:
output$plot <- renderPlot({
data <- df_products_upload() ........ #Some Analysis
data2, data3.... # Items generated during analysis
plot(data2)})
and then complete the analysis inside of the output function and create my additional data frames that will eventually be utilized in calls. But if I want to create multiple output plots that will be generated in the dashboard, is there better efficiency to allow the analysis to be run so future output plots can be generated this way?
output$plot2 <- renderPlot({
plot(data3) })
Currently I can only get it work by duplicating the analysis for all outputs in the server which is very inefficient.
output$plot2 <- renderPlot({
data <- df_products_upload() ........ #Some Analysis
data2, data3.... # Items generated during analysis
plot(data3)})
Thanks!
It seems to me that what you need is
data<-reactive({
data <- df_products_upload() ........ #Some Analysis
data #data should be a vector, list or whatever containg all the itmes
})
and then
output$plot <- renderPlot({
plot(data()[1])
})
output$plot2 <- renderPlot({
plot(data()[2])
})
Related
In my app I want user to choose between two dataset (mtcars and iris), then to choose one variable from previously chosen dataset. The app will show the summary of that variable.
I can choose the dataset, however a problem arises when I'm trying to choose the variable - everytime I choose something, it returns to the first column available (eg. while choosing Iris as a db I can see only Sepal.Length). For a brief moment summary can be seen for that chosen variable
library(shiny)
library(dplyr)
db_cars <- mtcars
db_iris <- iris
# Define server logic required to summarize and view the selected dataset
server <- shinyServer(function(input, output, session) {
datasetInput <- reactive({
simple_name <- eval(as.symbol(paste(input$dataset)))
observe({
updateSelectInput(
session,
"variable",
choices = colnames(simple_name[1:ncol(simple_name)])
)
})
simple_name %>% pull (input$variable)
})
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
})
# Define UI for dataset viewer application
ui <- shinyUI(pageWithSidebar(
headerPanel("Shiny Text"),
sidebarPanel(
selectInput("dataset", "Choose dataset",
choices = c("db_cars", "db_iris")),
selectInput("variable", "Choose variable",
choices = ""),
),
mainPanel(
verbatimTextOutput("summary"),
)
))
shinyApp(ui = ui, server = server)
You should separate the reactive element into two parts. One for the data selection, and one for variable selection.
server <- shinyServer(function(input, output, session) {
currentdata <- reactive({
simple_name <- eval(as.symbol(paste(input$dataset)))
updateSelectInput(
session,
"variable",
choices = colnames(simple_name[1:ncol(simple_name)])
)
simple_name
})
datasetInput <- reactive({
currentdata() %>% pull(input$variable)
})
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
})
They way each is only dependent on one input value so you don't run into trouble trying to change the dataset when you are trying to change just the varible.
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'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])
}
As, I am new to shiny apps need some assistance, uploading excel file and generating table output in shiny app works fine, but can't able to download the plot to a pdf format
Here is my code
library(shiny)
library(openxlsx)
library(lattice)
runApp(
list(
ui = fluidPage(
titlePanel("plots"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose xlsx file',
accept = c(".xlsx")),
tags$hr(),
downloadButton('down',"download plot")
),
mainPanel(
tableOutput('contents'),
plotOutput('plot'))
)
),
server = function(input, output){
output$contents <- renderTable({
inFile <- input$file1
if(is.null(inFile))
return(NULL)
else
read.xlsx(inFile$datapath)
})
plotInput <- reactive({
df <- input$file1
xyplot(df[,2]~df[,1],df(),xlim=c(0,10),ylim=c(0,100),type = "b")
})
output$plot <- renderPlot({
print(plotInput())
})
output$down <- downloadHandler(
filename = function(){paste("plot",".pdf",sep=".") },
content = function(file) {
pdf(file)
xyplot(df[,2]~df[,1],df(),xlim=c(0,10),ylim=c(0,100),type = "b")
dev.off()
}
)
}
)
)
The problem was that in some parts of your code you were accessing a dynamic data frame via df() but you had never defined it.
In this kind of problem, it is best to create a reactive data frame, say, df which contains the uploaded data and is passed to other reactive parts of the code via df().
Full example:
library(shiny)
library(openxlsx)
library(lattice)
runApp(
list(
ui = fluidPage(
titlePanel("plots"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose xlsx file',
accept = c(".xlsx")),
tags$hr(),
downloadButton('down',"download plot")
),
mainPanel(
tableOutput('contents'),
plotOutput('plot'))
)
),
server = function(input, output){
df <- reactive({
inFile <- input$file1
req(inFile) # require that inFile is available (is not NULL)
# (a user has uploaded data)
# read.xlsx(inFile$datapath)
head(iris, 10)
})
output$contents <- renderTable({
# access uploaded data via df()
df()
})
plotInput <- reactive({
df <- df()
xyplot(df[,2]~df[,1], df ,xlim=c(0,10),ylim=c(0,100),type = "b")
})
output$plot <- renderPlot({
plotInput()
})
output$down <- downloadHandler(
filename = function(){paste("plot",".pdf",sep=".") },
content = function(file) {
pdf(file)
#xyplot(df[,2]~df[,1],df(),xlim=c(0,10),ylim=c(0,100),type = "b")
# you have to print the plot so that you can open pdf file
print(plotInput())
dev.off()
}
)
}
)
)
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()
})
}
)
)