In my shiny app, I am uploading different types of files which I do want to process and display the result on different output sections. But the second output is depended on the first output result. In my second output, I am filtering the first table using column names. Now the problem I have is some files I am uploading don't have the columns I am using to subset for second output, they have been moved to the first row which means once I upload a file with different columns I should replace the columns existing with first row them filter results for the second output. Here is my app :
library(shiny)
library(DT)
ui<- shinyUI(fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')),
# radio button to show either row or replaced column table
radioButtons("radio", label = h3("Replace columns"),
choices = list("Raw table" = 1, "change columns" = 2),
selected = 1)
),
mainPanel(
DT::dataTableOutput('contents'),
DT::dataTableOutput('filtered')
)
)
)
)
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 <- DT::renderDataTable({
DT::datatable(myData())
})
#Replace columns reactive event
replaceColumns <- eventReactive(input$radio,{
#Change row to column and delete first row
colnames(myData()) <-myData()[1,]
df = myData()[-1, ]
df
})
data2<- reactive({
# Select columns of the dataframe
df1 <- select(myData(),mpg,cyl,wt)
df1
})
#Output based on either raw or replaced column table
output$filtered <- DT::renderDataTable({
DT::datatable(data2())
})
}
shinyApp(ui,server)
How can Use radio button or if there is another better way, so that when I upload a file and I see it has right columns, it automatically displays in first output, and when I click raw table In radio button it proceeds to filter and give filtered output and If uploaded file does not have right columns I click on change columns so that to replace columns with the first row of the table then filter and show filtered output?
The way I want my app to behave is, when I upload a file, it displays and if the file has right columns, then I click raw table on radio button which now proceeds to filter and display filtered output in second output but if I can see the file uploaded has wrong columns then I click on change columns in radio button which will replace columns with the first row and then proceed to filtered results in second output. What I simply mean is. I want the second output to depend on what I select on radio button.
In both cases, the first DT output shows the table as loaded (with good or bad names). One approach is to fix the column names silently:
library(shiny)
library(DT)
ui <- shinyUI(fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(fileInput(
'file1',
'Choose CSV File',
accept = c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')
)),
mainPanel(
DT::dataTableOutput('contents'),
DT::dataTableOutput('filtered')
)
)
))
server <- function(input, output, session) {
myData <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
data <- read.csv(inFile$datapath, header = TRUE)
data
})
# Approach 1: fix the data silently
# Replace column names if needed
fixData <- reactive({
req(input$file1)
df <- myData()
expectedColumns <- c("mpg", "cyl", "wt")
if (!all(expectedColumns %in% colnames(myData()))) {
#Change row to column and delete first row
colnames(df) <- df[1, ]
df = df[-1,]
}
df
})
output$contents <- DT::renderDataTable({
DT::datatable(myData())
})
data2 <- reactive({
# Select columns of the dataframe
df1 <- select(fixData(), mpg, cyl, wt)
df1
})
#Output based on either raw or replaced column table
output$filtered <- DT::renderDataTable({
DT::datatable(data2())
})
}
runApp(list(ui = ui, server = server))
Another approach is to use radio buttons as you have planned:
library(shiny)
library(DT)
ui<- shinyUI(fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')),
# radio button to show either row or replaced column table
radioButtons("radio", label = h3("Replace columns"),
choices = list("Raw table" = 1, "Change column names" = 2),
selected = 1)
),
mainPanel(
DT::dataTableOutput('contents'),
DT::dataTableOutput('filtered')
)
)
)
)
server <- function(input, output, session){
myData <- reactive({
inFile <- input$file1
if (is.null(inFile)) return(NULL)
data <- read.csv(inFile$datapath, header = TRUE)
data
})
# Approach 2: click on an radioButton to fix the data
fixData <- reactive({
req(input$file1)
df <- myData()
if (input$radio == 2) {
#Change row to column and delete first row
colnames(df) <- df[1,]
df = df[-1, ]
}
df
})
output$contents <- DT::renderDataTable({
DT::datatable(myData())
})
data2<- reactive({
# Select columns of the dataframe
df1 <- select(fixData(),mpg,cyl,wt)
df1
})
#Output based on either raw or replaced column table
output$filtered <- DT::renderDataTable({
DT::datatable(data2())
})
}
runApp(list(ui=ui,server=server))
Related
I'm struggeling with this one for hours:
In my app a simple test dataset df gets loaded upon starting the app. The user then may add further datasets through a file upload before selecting from a dropdown menu (here selectInput) the dataset he likes to continue working with.
What I'm failing to do:
After starting the app, the reactive df_list should only contain the initial dataset df and the dropdown menu should only hold the values c("", "df"). After adding a dataset through an upload (or else) df_list should be expanded (and the dropdown accordingly). So that I have a list containing all available datasets the user can select from.
But I only manage to create two scenarios: the dropdown menu contains df but I fail to expand the df_list after adding a dataset. Or the dropdown menu stays empty until I add a dataset, so the user has first to add a dataset before he can work with the test dataset.
My code example: I 'simulate' a file upload via an actionButton that creates the data.frame df_upload. Here follows the example without trying to expand df_list with the additional dataset df_upload.
library(shiny)
# df available from start
df <- data.frame(Var = 1:10)
ui <- fluidPage(
selectInput("select", label = "Select data", choices = c("")),
actionButton("upload", "Simulate Upload"),
tableOutput("tabdata")
)
server <- function(input, output, session) {
# reactive that lists all datasets
df_list <- reactive({list(df = df)})
# 'upload' of second df
df_upload <- eventReactive(input$upload, {
data.frame(Var = 11:20)
})
# observes if df_list() gets expanded to update choices
observeEvent(df_list(), {
updateSelectInput(session = session,
inputId = "select",
choices = c("", names(df_list())))
})
# output of selected dataset
output$tabdata <- renderTable({
req(df_list())
df_list()[[input$select]]
})
}
shinyApp(ui, server)
Here one of many things I tried (this adds df_upload succesfully, but fails to show df initially in the dropdown menu after starting the app):
library(shiny)
# df available from start
df <- data.frame(Var = 1:10)
ui <- fluidPage(
selectInput("select", label = "Select data", choices = c("")),
actionButton("upload", "Simulate Upload"),
tableOutput("tabdata")
)
server <- function(input, output, session) {
# reactive that lists all datasets
df_list <- reactive({
df_list <- list(df = df)
# check if there is an uploaded df, and if yes add it to df_list
# does not work, because it does not give me df_list only containing df
# in case no dataset was added yet.
# is.null is not the proper way, because if df_upload does not exist yet,
# it does not yield NULL. I also tried it unsuccessfully
# with exists("df_upload()")
if (!is.null(df_upload())) {
df_list[[2]] <- df_upload()
names(df_list)[2] <- "df_upload"
}
return(df_list)
})
# 'upload' of second df
df_upload <- eventReactive(input$upload, {
data.frame(Var = 11:20)
})
# observes if df_list() gets expanded to update choices
observeEvent(df_list(), {
updateSelectInput(session = session,
inputId = "select",
choices = c("", names(df_list())))
})
# output of selected dataset
output$tabdata <- renderTable({
req(df_list())
df_list()[[input$select]]
})
}
shinyApp(ui, server)
A simple solution using reactiveValues based on #Limey's comment:
library(shiny)
# df available from start
df <- data.frame(Var = 1:10)
reactlog::reactlog_enable()
ui <- fluidPage(
selectInput("select", label = "Select data", choices = c("df")),
actionButton("upload", "Simulate Upload"),
tableOutput("tabdata")
)
server <- function(input, output, session) {
# empty reactiveValues rv to store all datasets in
rv <- reactiveValues()
# store the test df in rv
rv$df <- df
# 'upload' of second df and storing it in rv
observeEvent(input$upload, {
rv$df_upload <- data.frame(Var = 11:20)
})
# update selectInput choices
observe({
updateSelectInput(session = session,
inputId = "select",
choices = names(rv),
selected = "df")
})
# output of selected dataset
output$tabdata <- renderTable({
rv[[input$select]]
})
}
shinyApp(ui, server)
I am new to shiny, I sm trying to save reactive tables based on user selection from a dropdown menu. I would like my dropdown menu to have a list of reactive tables that a user can select then click the save button to save the table selected, this is what I tried but the seems the dropdown doesn't select the reactive tables
# Set libraries
library(shiny)
library(shinyFiles)
# Shiny app with two fields that the user can submit data for
shinyApp(
ui = fluidPage(
DT::dataTableOutput("responses", width = 300), tags$hr(),
selectInput("data", "data:",
choices=(df1(),df2())),
tags$hr(),
shinySaveButton("save", "Save file", "Save file as ...", filetype="csv")
#actionButton("submit", "Submit")
),
server = function(input, output, session) {
#create dataframe one
df1<- reactive({
df <- data.frame("id" = 1:2, "Age" = c(21,15), "Name" = c("John","Dora"))
df
})
#create dataframe two
df2<- reactive({
df <- data.frame("id" = 2:4, "Age" = c(521,715), "Name" = c("Hellen","Jane"))
df
})
##########################################
observeEvent(input$save,{
volumes <- c("UserFolder"="path")
shinyFileSave(input, "save", roots=volumes, session=session)
fileinfo <- parseSavePath(volumes, input$save)
data <- input$data
if (nrow(fileinfo) > 0) {
write.csv(data, as.character(fileinfo$datapath))
}
})
})
My end result should be an app where a user selects a reactive table to save from dropdown then go ahead and click save button. The reason I opted for this option is that I have more reactive tables generated in the app that I would like to save each on its own directory.
Let me know if this is what you're looking for.
I set the selectInput to list 2 strings as default data frames to be downloaded:
selectInput("data", "data:", choices=c("df1", "df2"))
This can be dynamically updated with updateSelectInput as mentioned in comments.
You can have one reactive variable to determine what data to view and save df. output$responses will show the table. Both this function and the observeEvent will call df to get data, and df will provide the relevant data frame based on whatever the selectInput is set to.
# Set libraries
library(shiny)
library(shinyFiles)
library(DT)
# Shiny app with two fields that the user can submit data for
shinyApp(
ui = fluidPage(
dataTableOutput("responses", width = 300),
tags$hr(),
selectInput("data", "data:", choices=c("df1", "df2")),
tags$hr(),
shinySaveButton("save", "Save file", "Save file as ...", filetype="csv")
#actionButton("submit", "Submit")
),
server = function(input, output, session) {
#data will contain selected dataframe
df <- reactive({
switch (input$data,
"df1" = data.frame("id" = 1:2, "Age" = c(21,15), "Name" = c("John","Dora")),
"df2" = data.frame("id" = 3:4, "Age" = c(521,715), "Name" = c("Hellen","Jane"))
)
})
#show selected data in data table
output$responses <- renderDataTable({
df()
})
##########################################
observeEvent(input$save,{
volumes <- c("UserFolder"="path")
shinyFileSave(input, "save", roots=volumes, session=session)
fileinfo <- parseSavePath(volumes, input$save)
data <- df()
if (nrow(fileinfo) > 0) {
write.csv(data, as.character(fileinfo$datapath))
}
})
})
How can I create a shiny app with two dropdown menus with csv files from ./data folder then read those Csv and compare differences?
A user selects CSV from two dropdown menus then automatically generates differences
UI.R
library("shiny")
ui <- fluidPage(
fluidPage(
titlePanel("Automated Data Dictionary Comparison"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = 'Dic1',
label = 'Choose First Data Dictionary:',
choices = list.files(path = "./data",
full.names = FALSE,
recursive = FALSE)),
selectInput(inputId = 'Dic2',
label = 'Choose Second Data Dictionary:',
choices = list.files(path = "./data",
full.names = FALSE,
recursive = FALSE))
),
mainPanel(
tableOutput('contents')
)
)
)
)
SERVER.R
Library(shiny)
library(dplyr)
server <- function(input, output) {
dataset <- reactive({
infile <- input$Dic1
if (is.null(infile)){
return(NULL)
}
read.csv(infile[[1]])
})
output$contents <- renderDataTable({
#x <- dataset()
Diff <- render_diff(diff_data(data_ref=input$DIC1, data = input$DIC2),
Diff
})
}
From what I can see here, what you are doing is that you are correctly creating your reactive dataset object dataset (for 1 of your input files though not both), but you are not using this later on, when you want to generate the differences table (which again needs to be a reactive component as it will be generated from 2 reactive ones - dataset1 and dataset2).
Something like this should do the trick though (wrap it inside the server function):
# Parse first file
dataset1 <- reactive({
infile <- input$Dic1
if (is.null(infile)){
return(NULL)
}
x <- read.csv(infile[[1]])
x
})
# Parse second file
dataset2 <- reactive({
infile <- input$Dic2
if (is.null(infile)){
return(NULL)
}
x <- read.csv(infile[[1]])
x
})
# Create comparison table (reactive as both of its elements are reactive)
diff <- reactive({
x <- render_diff(diff_data(data_ref=dataset1(), data=dataset2()))
x
})
#Output
output$contents <- renderDataTable({
diff()
})
Check the above and let me know how it goes for you.
I have a shiny app where I want to allow the user to select a dataset based on a set of uploaded files and then specify the columns to display from the selected dataset. If I leave some columns selected and then switch datasets, an error flashes and is output to the console stating that the selected columns are unknown before the app switches datasets and displays it correctly. In my full app however, the app crashes, though I wasn't able to figure out how to reproduce the crash. I thought it might be related to some preprocessing that is done to add additional columns which are the same across datasets and which remain selected, but the error is the same without that feature.
library(shiny)
library(tidyverse)
library(DT)
ui <- fluidPage(
checkboxGroupInput("select_var", label = "Select Variables"),
selectInput("dataset", label = NULL, choices = c("mtcars", "rock")),
DT::dataTableOutput("table")
)
server <- function(session, input, output) {
# define the dataset
data <- reactive({switch(input$dataset,"rock" = rock,"mtcars" = mtcars)})
# add a common column name that is always selected
dataprocessed <- reactive({data <- data()
data$num <- seq(1:nrow(data))
return(data)})
# dynamically generate the variable names
observe({
vchoices <- names(dataprocessed())
updateCheckboxGroupInput(session, "select_var", choices = vchoices, selected = c("num"))
})
# select the variables based on checkbox
data_sel <- reactive({
req(input$select_var)
df_sel <- dataprocessed() %>% select(input$select_var)
})
output$table <- DT::renderDataTable(data_sel())
}
# Run the application
shinyApp(ui = ui, server = server)
We can add a conditional requirement using req() to test for column existence before rendering:
library(shiny)
library(tidyverse)
library(DT)
ui <- fluidPage(
checkboxGroupInput("select_var", label = "Select Variables"),
selectInput("dataset", label = NULL, choices = c("mtcars", "rock")),
DT::dataTableOutput("table")
)
server <- function(session, input, output) {
# define the dataset
data <- reactive({
switch(input$dataset,"rock" = rock,"mtcars" = mtcars)
})
# add a common column name that is always selected
dataprocessed <- reactive({
data <- data()
data$num <- seq(1:nrow(data))
return(data)
})
# dynamically generate the variable names
observe({
vchoices <- names(dataprocessed())
updateCheckboxGroupInput(session, "select_var", choices = vchoices, selected = c("num"))
})
# select the variables based on checkbox
data_sel <- reactive({
req(input$select_var)
req(names(dataprocessed()) %in% input$select_var)
a <- names(dataprocessed())[names(dataprocessed()) %in% input$select_var]
df_sel <- dataprocessed() %>% select(a)
})
output$table <- DT::renderDataTable(data_sel())
}
# Run the application
shinyApp(ui = ui, server = server)
Hey I have a Shiny App which input Data and work with them. But now I want an actionbutton which select the Factors for a subset funktion. Problem is that when I start the App and input the Data, the Factors are not there, just after the first click on the action button the factors get the column names of the Data.
How can I program this so that the factors are displayed to me directly after reading the data. Thank you for your help!
Here a part of the UI and Server
radioButtons(
"fileType_Input",
label = h5("Choose file type and click on browse"),
choices = list(".csv" = 1, ".xlsx" = 2),
selected = 1,
inline = TRUE
),
fileInput('file1', '' ),
selectInput("letters", label=NULL, factors, multiple = TRUE),
actionButton("choose", "Auswahl"),
verbatimTextOutput("list")
Server:
shinyServer(function(input, output, session) {
# Get the upload file
myData <- reactive({
inFile <- input$file1
if (is.null(inFile)) {
return(NULL) }
if (input$fileType_Input == "1") {
read.csv2(inFile$datapath,
header = TRUE,
stringsAsFactors = FALSE)
} else {
read_excel(inFile$datapath)
}
})
# When the Choose button is clicked, add the current letters to v$choices
# and update the selector
observeEvent(input$choose, {
data <- myData()
factors <- colnames(data) # get the names of the Factors in a Vector to select them
v$choices <- input$letters # append(v$choices,input$letters)
updateSelectInput(session, "letters",
choices = factors #[!factors %in% v$choices)]
)
})
output$list <- renderPrint({
v$choices
})
Instead of putting your code in observeEvent for input$choose you could put it in observe. Something like this:
observe({
if(!is.null(myData()))
{
data <- myData()
factors <- colnames(data) # get the names of the Factors in a Vector to select them
v$choices <- input$letters # append(v$choices,input$letters)
updateSelectInput(session, "letters",
choices = factors #[!factors %in% v$choices)]
)
}
})
Hope it helps!