Below I have an R Shiny code to upload an input file to the application. All csv files that'll be uploaded will have a column 'carname'. I want to have a dropdown that's able to access and select choices in that column. How do I access the uploaded file in the ui?
Error: Error in data$carname : object of type 'closure' is not subsettable
ui <- fluidPage(
headerPanel("Webpapp"),
sidebarPanel(
fileInput(inputId = "filedata",
label = "Upload the Raw Data File",
accept = c("text/csv", "text/comma-separated-values,text/plain",
".csv")),
selectInput("cars", "Select car:", choices = data$carname, selected = "Nissan Sentra"),
),
mainPanel(
plotOutput('plot'),
)
)
# Define server code ####
server <- function(input, output) {
data <- reactive({
req(input$filedata)
read.csv(input$filedata$datapath, skip = 15, header = F)
})
output$plot <- renderPlot({
if (is.null(data())) {
return(NULL)
}
plot_probe(input$cars) #Plotting function in global
})
}
shinyApp(ui = ui, server = server)
This is not necessary and also I would say not recommend in this case to use renderUI. updateSelectInput() will be faster (Make R Shiny Dashboards Faster with updateInput, CSS, and JavaScript) and require less rewriting of code:
library(shiny)
ui <- fluidPage(
headerPanel("Webpapp"),
sidebarPanel(
fileInput(inputId = "filedata",
label = "Upload the Raw Data File",
accept = c("text/csv", "text/comma-separated-values,text/plain",
".csv")),
selectInput("cars", "Select car:", choices = "", selected = "Nissan Sentra"),
),
mainPanel(
plotOutput('plot'),
)
)
# Define server code ####
server <- function(session, input, output) {
data <- reactive({
req(input$filedata)
read.csv(input$filedata$datapath, skip = 15, header = F)
})
observe({
req(data())
updateSelectInput(session, "cars", choices = data()$carname)
})
output$plot <- renderPlot({
if (is.null(data())) {
return(NULL)
}
plot_probe(input$cars) #Plotting function in global
})
}
shinyApp(ui = ui, server = server)
What I have changed:
In UI part I passed "" to the choice argument in selectInput, because as #gdevaux mentioned, you cannot use in this way data from server in UI.
I have added session parameter to server function - it is necessary for update* functions.
I have added updateSelectInput() inside observer - it will update selectInput if data() won't be NULL.
As #gdevaux did, I have changed your choices = data$carname into choices = data()$carname. It is because data as a reactive you need to use as a function.
You cannot access the uploaded file from the UI.
But you can create an UI element from the server, and send it to the UI with the functions renderUI and UIOutput.
ui <- fluidPage(
headerPanel("Webpapp"),
sidebarPanel(
fileInput(inputId = "filedata",
label = "Upload the Raw Data File",
accept = c("text/csv", "text/comma-separated-values,text/plain",
".csv")),
uiOutput("ui_select")
),
mainPanel(
plotOutput('plot'),
)
)
# Define server code ####
server <- function(input, output) {
data <- reactive({
req(input$filedata)
read.csv(input$filedata$datapath, skip = 15, header = F)
})
# UI element for select input
output$ui_select <- renderUI({
req(data())
selectInput("cars", "Select car:", choices = data()$carname, selected = "Nissan Sentra")
})
output$plot <- renderPlot({
if (is.null(data())) {
return(NULL)
}
plot_probe(input$cars) #Plotting function in global
})
}
shinyApp(ui = ui, server = server)
Related
I have the following app which allows for text to be entered and it is then saved as VALUE and printed on a panel.
Although it looks like I can only do this with one text input at a time - even if I click add (so I don't believe this button is working). On top of that I would like for the user to be able to add multiple inputs (like I have below).
And then my VALUE function should be list with multiple inputs.
code below
library(shiny)
ui <- fluidPage(
headerPanel("R Package App"),
sidebarPanel(
# selectInput("options", "options", choices=c('abc','def')),
textInput("textbox", "Enter R Package Name", ""),
actionButton("add","Add")
),
mainPanel(
textOutput("caption")
)
)
server <- function(input, output, session) {
observe({
VALUE <- ''
if(input$add>0) {
isolate({
VALUE <- input$textbox
})
}
updateTextInput(session, inputId = "textbox", value = VALUE)
})
output$caption <- renderText({
input$textbox
})
}
shinyApp(ui = ui, server = server)
Have you considered using selectizeInput with it's create option?
library(shiny)
packagesDF <- as.data.frame(installed.packages())
ui <- fluidPage(
headerPanel("R Package App"),
sidebarPanel(
selectizeInput(
inputId = "selectedPackages",
label = "Enter R Package Name",
choices = packagesDF$Package,
selected = NULL,
multiple = TRUE,
width = "100%",
options = list(
'plugins' = list('remove_button'),
'create' = TRUE,
'persist' = TRUE
)
)
),
mainPanel(textOutput("caption"))
)
server <- function(input, output, session) {
output$caption <- renderText({
paste0(input$selectedPackages, collapse = ", ")
})
}
shinyApp(ui = ui, server = server)
I have the shiny app below in which the user uploads a csv. Then the pickerInput gets the unique values of the first column of that csv and uses them to subset the dataframe and display it in a table. The issue is that I want to use an actionButton in order to apply changes but when the csv is uploaded for first time it should be displayed full and not displayed empty because the actionButton is not triggered yet. For the purpose of the example I have used iris dataset instead of a csv.
# app.R ##
library(shiny)
library(DT)
library(shinyWidgets)
ui <- pageWithSidebar(
headerPanel('Iris k-means clustering'),
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
uiOutput("id"),
actionButton("go","Go")
),
mainPanel(
uiOutput('contents')
)
)
server <- function(input, output, session) {
output$id<-renderUI({
#inFile <- input$file1
#df2<-data.frame(read.csv(inFile$datapath, header = TRUE))
pickerInput("select", "Select ID",
choices = as.character(unique(iris$Species)),
multiple = T,options = list(`actions-box` = TRUE),
selected = as.character(unique(iris$Species)))
})
output$contents <- renderUI({
input$goButton
#inFile <- input$file1
#df<-data.frame(read.csv(inFile$datapath, header = TRUE))
df<-data.frame(iris)
df<-subset(iris,Species %in% isolate(input$select))
renderDataTable({
datatable(
df,
options = list(scrollX = TRUE,pageLength=5)
)
})
})
}
shinyApp(ui = ui, server = server)
library(shiny)
library(DT)
library(shinyWidgets)
ui <- pageWithSidebar(
headerPanel('Iris k-means clustering'),
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
uiOutput("picker"),
actionButton("go","Go")
),
mainPanel(
DTOutput("dtable")
)
)
server <- function(input, output, session) {
filteredCSV <- reactiveVal(NULL)
CSV <- eventReactive(input[["file1"]], {
dat <- read.csv(input[["file1"]]$datapath, header = TRUE)
filteredCSV(dat)
dat
})
output[["picker"]] <- renderUI({
req(CSV())
choices <- unique(as.character(CSV()[,1]))
pickerInput("select", "Select ID",
choices = choices,
multiple = TRUE, options = list(`actions-box` = TRUE),
selected = choices)
})
observeEvent(input[["go"]], {
req(CSV())
filteredCSV(CSV()[CSV()[,1] %in% input[["select"]],])
})
output[["dtable"]] <- renderDT({
req(filteredCSV())
datatable(
filteredCSV(),
options = list(scrollX = TRUE, pageLength = 5)
)
})
}
shinyApp(ui = ui, server = server)
This is in reply to the previous version of your post, but should solve the main problem - this is how I would go about it, using reactive expressions (and data.table, but you might just as well not use it):
library(shiny)
library(shinyWidgets)
library(DT)
library(data.table)
ui <- pageWithSidebar(
headerPanel('Iris k-means clustering'),
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
uiOutput("id"),
#actionButton("go","Go")
),
mainPanel(
DT::dataTableOutput('contents')
)
)
server <- function(input, output, session) {
getFile <- reactive({
req(input$file1)
fread(input$file1$datapath, header = TRUE)
})
output$contents <- DT::renderDataTable({
DT::datatable(getFile()[get(colnames(getFile())[1]) %in% input$select])
})
output$id <- renderUI({
req(getFile())
df <- getFile()
pickerInput("select", "Select ID",
choices = unique(df[[1]]),
multiple = TRUE, options = list(`actions-box` = TRUE),
selected = unique(df[[1]]))
})
}
shinyApp(ui = ui, server = 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 am trying to build a shiny app where I can upload a csv file and based on the column names, from selectInput - app shows basic statistic (graphs, descriptive statistic-average, sd, var .... and some statistical tests).
There is no problem with datasets, which are already in the app. Also no problem with statistics.
Problem is how to make working selectInput with the names of columns from new upload csv file.
Here is my light version of code:
UI:
library(shiny)
shinyUI(fluidPage(
titlePanel("xxx"),
sidebarLayout(
sidebarPanel(
fileInput("csvFile", "Drag cars.csv over here!"),
selectInput("var", "choose variable:", choices= names(data()))
),
mainPanel(
textOutput("average"),
plotOutput("plot")
)
)
))
SERVER:
shinyServer(function(input, output, session) {
data <- reactive({
file1 <- input$csvFile
if (is.null(file1)) {
return()
}
data = read.csv(file=file1$datapath)
data
})
output$average <- renderText({
paste("average is: ", mean(data()[,input$var]))
})
output$plot <- renderPlot({
boxplot(data()[,input$var])
})
})
You will need to use uiOutput() and renderUI() functions.
library(shiny)
ui <- fluidPage(
titlePanel("xxx"),
sidebarLayout(
sidebarPanel(
fileInput("csvFile", "Drag cars.csv over here!"),
uiOutput("var_ui")
),
mainPanel(
textOutput("average"),
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
data <- reactive({
file1 <- input$csvFile
if (is.null(file1)) {
return()
}
data = read.csv(file=file1$datapath)
data
})
output$average <- renderText({
paste("average is: ", mean(data()[,input$var]))
})
output$plot <- renderPlot({
boxplot(data()[,input$var])
})
output$var_ui <- renderUI({
selectInput("var", "choose variable:", choices= names(data()))
})
}
shinyApp(ui, server)
You can also use observe in server. observe does not return anything. Unlike reactive, it responds immediately (and not lazily). It's best used for ip/op operations.
observe({
updateSelectInput(
session,
"var",
choices = names(data())
)
)}
I am building an app in shiny (R). At the beginning the user can upload a file to use (I am doing a sort data analysis). My goal is to be able to use files without knowing how many columns this file has, and how the data exactly looks like.
So now I have to select the columns by number, and I made a small preview app for this to select columns and then display them next to the original:
library(shiny)
ui <-fluidPage(
headerPanel("Select data"),
sidebarLayout(
sidebarPanel(
fileInput("uploadFile", "XLSX file"),
textInput('vec1', 'Choose training columns', "3,4"),
actionButton("choose","choose data")
),
mainPanel(
fluidRow(
column(6,tableOutput("data_raw")),
column(6,tableOutput("data_selected"))
)
)
)
)
server <- function(input, output) {
output$data_raw <- renderTable({
inFile <- input$uploadFile
if (is.null(inFile))
return(NULL)
data_raw <<-read.xlsx(inFile$datapath, 1)
})
observe({
if(input$choose>0){
selectvec <- as.numeric(unlist(strsplit(input$vec1,",")))
output$data_selected <- renderTable(
data_selected<- data_raw[,selectvec]
)
}
})
}
shinyApp(ui,server)
Now I would like to be able to select the columns to use on basis of their header.
It feels unnatural: changing the app while running.. but in a reactive environment.. why not?
QUESTION: How can I change the UI while it is allready running, with values originating from the input?
kind regards,
Pieter
To make me feel not as dirty for answering this...I didn't debug or handle reactives properly. But here ya go. You need to respond to the file that is uploaded on the server side, extract the column names, and append thosed to the choices in a select input that then passes down to the table function as a column filter.
upload_app <- function(){
library(shiny)
ui <- bootstrapPage(
tags$div(class = "container",
column(3,
fluidRow(
fileInput(inputId = 'user_data',
label = 'Upload Data (csv)',
multiple = FALSE,
accept = c(
'text/csv',
'text/comma-separated-values',
'text/tab-separated-values',
'text/plain',
'.csv',
'.tsv'
))
),
fluidRow(
uiOutput('column_vars')
)
),
column(9,
tableOutput('filtered_table'))
)
)
server <- function(session, input, output){
var_table <- reactive({
var_data <- input$user_data
read.csv(var_data$datapath, header = TRUE,sep = ",", quote = '')
})
output$column_vars <- renderUI({
if(!is.null(var_table())){
selectInput(inputId = 'cols',
choices = colnames(var_table()),
multiple = T,
label = "Choose Columns")
}
})
output$filtered_table <- renderTable({
if(!is.null(var_table())){
if(length(input$cols)>0){
get_these <- input$cols
new_table <- var_table()[,c(get_these)]
}else {
new_table <- var_table()
}
}else {
new_table <- data.frame(data = 'Waiting')
}
return(new_table)
})
}
shinyApp(ui, server)
}