The example that I'm working with is the iris data. If the current data contains iris[1:15,], how can I upload a .csv file with more iris data and click a button to combine the uploaded data with the current data and save everything in one dataframe?
Here is what I have so far based on what I've read. I was able to create the fileInput and action button but I think my issue is with the reactive button. I'm not sure how to use it properly to achieve what I need.
library(shiny)
library(DT)
data1<-data.frame(iris[1:15,])
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
numericInput('num','Number of rows',value=10,min=0),
actionButton("update", "Combine Data")),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output) {
output$table <- renderTable({
head(data1,n=input$num)
})
x<-reactive({
req(input$file1)
df_uploaded <- read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote,
stringsAsFactors = FALSE)
data2<-data.frame(df_uploaded)
return(data2)
})
merged_data<-eventReactive(input$update,{
datam<-rbind.data.frame(data1,x())
return(datam)
})
# output$table <- renderTable({
# head(merged_data(),n=input$num)})
}
shinyApp(ui, server)
Thanks!
The main issue is that read.csv receiving invalid argument i.e. NULL for header, sep, quote as you don't have input$header, input$sep, input$quote in UI.
library(shiny)
library(DT)
data1<-data.frame(iris[1:15,])
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
numericInput('num','Number of rows',value=10,min=0),
actionButton("update", "Combine Data")),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output) {
# output$table <- renderTable({
# head(data1,n=input$num)
# })
x<-reactive({
req(input$file1)
df_uploaded <- read.csv(input$file1$datapath,
#you don't have these variables in the UI, so they will raise an error
#header = input$header,
#sep = input$sep,
#quote = input$quote,
stringsAsFactors = FALSE)
#No need data2 and return(data2) as read.csv returns data.frame by default
#data2<-data.frame(df_uploaded)
#return(data2)
})
merged_data<-eventReactive(input$update,{
datam<-rbind.data.frame(data1, x())
return(datam)
})
output$table <- renderTable({
head(merged_data(), n=input$num)})
}
shinyApp(ui, server)
Related
I have this Shiny App, and so far it works great but the data when it is uploaded does not look as nice as I would have hoped. How do I limit it so each page only has 5 rows, and how can I get rid of the clear background of the table (right now it alternates between white and no background for the rows)
Thank you!
library(shiny)
library(dplyr) # alternatively, this also loads %>%
library(shinyWidgets)
regex_to_apply <- "\\bMASTER DATA\\b|\\bSOURCE LIST\\b|\\bVALIDITY DATES\\b|\\bMRP CONTROLLER\\b|\\bPSV\\b|\\bELIGIBILITY\\b|\\bCOST\\b|\\bMARKETING EXCLUSION\\b|\\bEFFECTIVITY\\b|\\bMISSING\\b|\bbBLANK\\b"
ui <- fluidPage(
# use a gradient in background, setting background color to blue
setBackgroundColor(
#https://rdrr.io/cran/shinyWidgets/man/setBackgroundColor.html used this website for help on background color
color = c("#F7FBFF", "#2171B5"),
gradient = "radial",
direction = c("top", "left")
),
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
checkboxInput("header", "Header", TRUE),
# Button
downloadButton("downloadData", "Download"),
actionButton('apply_regex', 'Apply Regex')
),
mainPanel(
dataTableOutput("contents")
)
)
)
server <- function(input, output) {
rv <- reactiveValues()
observe({
req(input$file1)
# input$file1 will be NULL initially. After the user selects
# and uploads a file, it will be a data frame with 'name',
# 'size', 'type', and 'datapath' columns. The 'datapath'
# column will contain the local filenames where the data can
# be found.
inFile <- input$file1
if (is.null(inFile))
return(NULL)
rv$data <- read.csv(inFile$datapath, header = input$header, encoding = "UTF-8")
})
output$contents <- renderDataTable({
rv$data
})
output$downloadData <- downloadHandler(
filename = function() {
paste("myfile",Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(rv$data, file, row.names = FALSE)
}
)
observeEvent(input$apply_regex, {
rv$data <- rv$data %>%
mutate(close_notes = close_notes %>% as.character() %>% toupper()) %>%
filter(grepl(regex_to_apply, close_notes))
})
}
shinyApp(ui, server)
How do I limit it so each page only has 5 rows. Set 'options' argument of renderDataTable. Here is a reproducible example:
library(shiny)
library(DT)
ui <- fluidPage(
br(),
DT::dataTableOutput(outputId = "t1")
)
server <- function(input, output, session) {
output$t1 <- DT::renderDataTable(iris, options = list(pageLength = 5))
}
shinyApp(ui, server)
I am working with R shiny for pheatmap, I want to read files and draw heatmaps, but it did not work. The csv file could be read, however, the content could not be seen from the web, and the heatmap could not be drawn.
library(shiny)
library(pheatmap)
ui = fluidPage("Test",
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
checkboxInput("header", "Header", TRUE)
),
tabPanel('map',
sidebarLayout(
sidebarPanel('side',
actionButton('getHmap', 'get heatmap')
),
mainPanel('main',
plotOutput("themap")
)
))
)
server = function(input, output, session) {
a <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
tbl <- read.csv(inFile$datapath, header=input$header, sep=input$sep, dec = input$dec)
return(tbl)
})
output$table.output <- renderTable({
a()
})
observeEvent(input$getHmap, {
row.names(a) <- a$Name
a <- a[,-1]
a[is.na(a)] <- 0
output$themap = renderPlot({
pheatmap(a)
})
})
}
shinyApp(ui, server)
```[![The original data I used][1]][1]
[1]: https://i.stack.imgur.com/S83cH.png
This could be a full working example. This seems to work at my end. The following changes were made:
Added tableOutput("table.output") to ui
Simplified read.csv as inputs for sep and dec were missing
Created plotData function as eventReactive to plot heatmap with action button
Converted data to matrix before adding rownames for plot
The output$themap calls the plotData function
library(shiny)
library(pheatmap)
ui = fluidPage("Test",
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
checkboxInput("header", "Header", TRUE)
),
tabPanel('map',
sidebarLayout(
sidebarPanel('side',
actionButton('getHmap', 'get heatmap')
),
mainPanel('main',
plotOutput("themap"),
tableOutput("table.output")
)
))
)
server = function(input, output, session) {
a <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
tbl <- read.csv(inFile$datapath, header=input$header) #, sep=input$sep, dec = input$dec)
return(tbl)
})
output$table.output <- renderTable({
a()
})
plotdata <- eventReactive(input$getHmap, {
a <- as.matrix(a()[-1])
row.names(a) <- a()$Name
a[is.na(a)] <- 0
a
})
output$themap = renderPlot({
pheatmap(plotdata())
})
}
shinyApp(ui, server)
I am developing a R Shiny application which will rely on a module in hopes that I can re-use the module for uploading and displaying two different data sets. As of now, my code works but I think I could make it a little bit cleaner as I don't think I have gotten the module correct. By that I mean, how do I move this code snippet (below) out of the app_server and into the module server and then use the callModule function for two different datasets. Similarly, I probably need to remove this code: tableOutput("metacontent") from the app ui and have that call in the module ui. See the module ui, module server, app ui, and app server below code snippet. Any suggestions? Thanks!
#code snippet
output$metacontents <- renderTable({
metafile()
})
# Module UI
mod_dataInput_ui <- function(id, label) {
# Create a namespace function using the provided id
ns <- NS(id)
tagList(
# Input: Select a file ----
fileInput(ns("id"), label,
multiple = FALSE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv",
".tsv")),
# Input: Select separator ----
radioButtons(ns("sep"), "Separator",
choices = c(Comma = ",",
Tab = "\t"),
selected = "\t"))
}
# Module Server
mod_dataInput_server <- function(input, output, session) {
userFile <- reactive({
validate(need(input$id !="", "Please import a data file"))
input$id
})
datafile <- reactive({
utils::read.table(userFile()$datapath,
header = FALSE,
sep = input$sep,
row.names = NULL,
skip = 1,
stringsAsFactors = FALSE)
})
}
#App UI
app_ui <- function() {
tagList(
# Leave this function for adding external resources
golem_add_external_resources(),
# List the first level UI elements here
navbarPage("Tinsel",
tabPanel("Load Data",
sidebarPanel(mod_dataInput_ui("dataInput_ui_meta", tags$div("User META data", tags$br(), "(.csv, .tsv, or .txt file format)")), helpText("Can add help text here"),
# Horizontal line ----
tags$hr(style="border-color: black;"),
mod_dataInput_ui("dataInput_ui_gene", tags$div("User GENETIC data", tags$br(), "(.csv, .tsv, or .txt file format)")),
tags$hr(style="border-color: black;")),
mainPanel(
tabsetPanel(
tabPanel("Meta Data",
tableOutput("metacontents")),
tabPanel("Genetic Data",
tableOutput("genecontents"))
)))
)
)
}
#App server
app_server <- function(input, output, session) {
# List the first level callModules here
metafile <- callModule(mod_dataInput_server, "dataInput_ui_meta")
output$metacontents <- renderTable({
metafile()
})
genefile <- callModule(mod_dataInput_server, "dataInput_ui_gene")
output$genecontents <- renderTable({
genefile()
})
}
As far as I see it, you've gotten the module correctly: you're reusing the UI and server on two different IDs for the data import.
You can optimize what you've done by creating a module for the table part, so writing:
# mod_table.R
mod_table_ui <- function(id, name){
ns <- NS(id)
tabPanel(
name,
tableOutput(ns("metacontents"))
)
}
# Module Server
#' #rdname mod_table
#' #export
#' #keywords internal
mod_table_server <- function(input, output, session, file){
ns <- session$ns
output$metacontents <- renderTable({
file()
})
}
And then in app_ui:
#' #import shiny
app_ui <- function() {
tagList(
# Leave this function for adding external resources
golem_add_external_resources(),
# List the first level UI elements here
navbarPage(
"Tinsel",
tabPanel(
"Load Data",
sidebarPanel(
mod_dataInput_ui(
"dataInput_ui_meta",
tags$div(
"User META data",
tags$br(),
"(.csv, .tsv, or .txt file format)"
)
),
helpText("Can add help text here"),
# Horizontal line ----
tags$hr(style="border-color: black;"),
mod_dataInput_ui(
"dataInput_ui_gene",
tags$div(
"User GENETIC data",
tags$br(),
"(.csv, .tsv, or .txt file format)"
)
),
tags$hr(style="border-color: black;")
),
mainPanel(
tabsetPanel(
mod_table_ui("table_ui_1", "Meta Data"),
mod_table_ui("table_ui_2", "Genetic Data")
)
)
)
)
)
}
And app_server:
app_server <- function(input, output, session) {
# List the first level callModules here
metafile <- callModule(mod_dataInput_server, "dataInput_ui_meta")
callModule(mod_table_server, "table_ui_1", metafile)
genefile <- callModule(mod_dataInput_server, "dataInput_ui_gene")
callModule(mod_table_server, "table_ui_2", genefile)
}
Let me know if that answers your question.
This is how I would do it.
library(shiny)
library(ggplot2)
#ui.R
ui <- fluidPage(
titlePanel("My shiny app"), sidebarLayout(
sidebarPanel(
helpText("This app shows how a user can upload a csv file. Then, plot the data.
Any file can be uploaded but analysis is only available
if the data is in same format as the sample file, downloadable below
"),
a("Data to be plotted", href="https://www.dropbox.com/s/t3q2eayogbe0bgl/shiny_data.csv?dl=0"),
tags$hr(),
fileInput("file","Upload the file"),
h5(helpText("Select the read.table parameters below")),
checkboxInput(inputId = 'header', label = 'Header', value = TRUE),
checkboxInput(inputId = "stringAsFactors", "stringAsFactors", FALSE),
br(),
radioButtons(inputId = 'sep', label = 'Separator', choices = c(Comma=',',Semicolon=';',Tab='\t', Space=''), selected = ',')
),
mainPanel(
uiOutput("tb"),
plotOutput("line")
)
)
)
#server.R
server <- function(input,output){
data <- reactive({
file1 <- input$file
if(is.null(file1)){return()}
read.table(file=file1$datapath, sep=input$sep, header = input$header, stringsAsFactors = input$stringAsFactors)})
output$filedf <- renderTable({
if(is.null(data())){return ()}
input$file
})
output$sum <- renderTable({
if(is.null(data())){return ()}
summary(data())
})
output$table <- renderTable({
if(is.null(data())){return ()}
data()
})
output$line <- renderPlot({
if (is.null(data())) { return() }
print(ggplot(data(), aes(x=date, y=aa)) + geom_line()+ facet_wrap(~station)) })
output$tb <- renderUI({if(is.null(data()))
h5()
else
tabsetPanel(tabPanel("About file", tableOutput("filedf")),tabPanel("Data", tableOutput("table")),tabPanel("Summary", tableOutput("sum")))
})
}
shinyApp(ui = ui, server = server)
I was trying to simplify my shiny app. However, as much as I try it is not working, as I would like it to.
My Idea was to load data to the app, perform some analyses and return intermediate results to the user. At the moment I have to load the data, choose the right columns etc. for each output I am generating:
ui <- shinyServer(
fluidPage(
tabsetPanel(
tabPanel("Data upload",
titlePanel("Data upload"),
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File",multiple = TRUE, accept = c("text/csv","text/comma-separated-values,text/plain",".csv")),
tags$hr(),
checkboxInput("header", "Header", TRUE), radioButtons("sep", "Separator", choices = c(Comma = ",", Semicolon = ";",Tab = "\t"), selected = ","),
tags$hr(),
checkboxInput("disp", "Display",TRUE),
tags$hr(),
uiOutput("choose_first_column"),
uiOutput("choose_second_column"),
br()
),
mainPanel(
tableOutput("contents"),
tags$hr(),
tableOutput("tab")
)
)
),
tabPanel("2","2"
)
)
)
)
server <- shinyServer(
function(input, output) {
observe({
req(input$file1)
df <- read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
output$contents <- renderTable({
(head(df))})
output$choose_first_column <- renderUI({
colnames <- names(df)
selectInput("column_1", "Choose Date column",
choices = colnames,
selected = colnames)})
output$choose_second_column <- renderUI({
colnames <- names(df)
selectInput("column_2", "Choose Variable column",
choices = colnames,
selected = colnames)})
output$tab <- renderTable({
req(input$file1)
df2 <- read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
df2 <- df2[, c(input$column_1,input$column_2), drop = FALSE]
return(head(df2))})
})
})
runApp(list(ui = ui, server = server))
It works, but as I usually have many data and I want to perform a couple of analyses, it is gets quite time-consuming to load and process the data for each “output content”.
Is there a way to avoid this? Could I for example load the data and choose the right columns globaly, as in the second example? (I crossed out the lines where the error occurs)
ui <- shinyServer(
fluidPage(
tabsetPanel(
tabPanel("Data upload",
titlePanel("Data upload"),
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File",multiple = TRUE, accept = c("text/csv","text/comma-separated-values,text/plain",".csv")),
tags$hr(),
checkboxInput("header", "Header", TRUE), radioButtons("sep", "Separator", choices = c(Comma = ",", Semicolon = ";",Tab = "\t"), selected = ","),
tags$hr(),
checkboxInput("disp", "Display",TRUE),
tags$hr(),
uiOutput("choose_first_column"),
uiOutput("choose_second_column"),
br()
),
mainPanel(
tableOutput("contents"),
tags$hr(),
tableOutput("tab")
)
)
),
tabPanel("2","2"
)
)
)
)
server <- shinyServer(
function(input, output) {
observe({
req(input$file1)
df <- read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
output$contents <- renderTable({
(head(df))})
output$choose_first_column <- renderUI({
colnames <- names(df)
selectInput("column_1", "Choose Date column",
choices = colnames,
selected = colnames)})
output$choose_second_column <- renderUI({
colnames <- names(df)
selectInput("column_2", "Choose Variable column",
choices = colnames,
selected = colnames)})
# df <- df[, c(input$column_1,input$column_2), drop = FALSE]
#
# output$tab <- renderTable({
# (head(df))})
})
})
runApp(list(ui = ui, server = server))
Input data example:
date time level
01.01.2000 00:00:00 0.3724
01.01.2000 01:00:00 0.192
01.01.2000 02:00:00 -0.0252
I would appreciate any help!
Aishe
From what I've understood, you are getting an error because the dataframe df that you have defined is not reactive. You should make it reactive as it will change every time the user selects input columns.
Refer this to read about reactivity. Change the deleted portion of your code to this:
df.selected.columns <- df[c(input$column_1,input$column_2)]
output$tab <- renderTable({
(head(df.selected.columns()))
})
I got stuck on my first shiny app again. So far the App was runing fine, but now I wanted to download the plot I generated and I can not work out how to get the results out of the observe function.
As I can not generate the plot outside the observe function, I was thinking I would assign the necessary data to a global variable useing <<-, but if I run a reactive function e.g. df.selected.columns() this seem to cause errors.
Can someone give me a hint how to proceed?
Thank you so much for any suggestions! Aishe
Here is me code:
ui <- shinyServer(
fluidPage(
tabsetPanel(
tabPanel("Data upload",
titlePanel("Data upload"),
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File",multiple = TRUE, accept = c("text/csv","text/comma-separated-values,text/plain",".csv")),
tags$hr(),
checkboxInput("header", "Header", TRUE), radioButtons("sep", "Separator", choices = c(Comma = ",", Semicolon = ";",Tab = "\t"), selected = ","),
tags$hr(),
checkboxInput("disp", "Display",TRUE),
tags$hr(),
uiOutput("choose_first_column"),
uiOutput("choose_second_column"),
br()
),
mainPanel(
tableOutput("contents"),
tags$hr(),
tableOutput("tab"),
tags$hr(),
uiOutput("download"),
plotOutput("headplot")
)
)
),
tabPanel("2","2"
)
)
)
)
server <- shinyServer(
function(input, output) {
observe({
req(input$file1)
df <- read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
output$contents <- renderTable({
(head(df))})
output$choose_first_column <- renderUI({
colnames <- names(df)
selectInput("column_1", "Choose Date column",
choices = colnames,
selected = colnames)})
output$choose_second_column <- renderUI({
colnames <- names(df)
selectInput("column_2", "Choose Variable column",
choices = colnames,
selected = colnames)})
df.selected.columns <- reactive({
df.columns <- df[,c(input$column_1,input$column_2)]
return(df.columns)
})
output$tab <- renderTable({
(head(df.selected.columns()))
})
Plot1 <- reactive({
plot(head(df.selected.columns()[,2]))
})
output$headplot <- renderPlot({
Plot1()
})
# This comes closest to what I wanted to do. However, now I can not select the columns anymore.
# try(result <<- head(df.selected.columns()[,2]),silent=T)
# With this line it crushes straight away
# result <<- head(df.selected.columns()[,2])
})
output$download <- renderUI({
if(!is.null(input$column_1) & !is.null(input$column_2)) {
downloadButton('OutputPlot', 'Download Plot')
}
})
output$OutputPlot <- downloadHandler(
filename = function() {
paste('plot', '.pdf', sep='')
},
content=function(file){
pdf(file)
plot(result)
dev.off()
})
})
runApp(list(ui = ui, server = server))
Input data example:
date time level
01.01.2000 00:00:00 0.3724
01.01.2000 01:00:00 0.192
01.01.2000 02:00:00 -0.0252
Remove the observe
Make the loaded file a reactive
Update all references to df to df() since it's now a reactive expression
Add appropriate req() functions to prevent error messages
In your downloadHandler you have plot(result), but there's no such thing as result. You want Plot() or plot(df.selected.columns())
You should be confirming that your selected delimiter is actually splitting the loaded table correctly before your return the loaded table. Without that, you'll get errors and strange results/
Here's the updated df and downloadHandler functions to get you started:
df <- reactive({
req(input$file1)
read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
})
output$OutputPlot <- downloadHandler(
filename = function() {
paste('plot', '.pdf', sep='')
},
content=function(file){
pdf(file)
plot(head(df.selected.columns()[,2]))
dev.off()
})