Using stackoverflow, I created a shiny app which uploads a csv file and then displays a datatable.
After selecting columns dynamically, where some columns have "_down" end.
I require help in shortening the dataframe (as in the code below) and also remove duplicates by ID column (if present).
# install.packages("shiny")
# install.packages("DT")
# install.packages("shinycssloaders")
library(DT)
library(shiny)
library(shinycssloaders)
UI code
##Creating the UI as a fluidPage,
##fluidPage allows scaling components of the browser in realtime to fill all available broswer width
##This is standard
ui <- fluidPage(
# Title of app
titlePanel("Upload file to table"),
# Main panel for displaying outputs
mainPanel(
#fileInput with acceptance of text/csv and more
fileInput('file', 'Choose file to upload',
accept = c(
'text/csv',
'text/comma-separated-values',
'text/tab-separated-values',
'text/plain',
'.csv',
'.tsv',
'.html'
)),
# Output: datatable
DT::dataTableOutput("data_as_table")%>%withSpinner(),
#Download button
downloadButton("downloadData", "Download")
)
)
Server Code
Creating server
server <- function(input, output) {
#Data is a reactive element meaning it will update when the reactive input inside it change
#Data will update when input$file changes
#input$file is the uploaded file (se fileInput in ui)
data <-reactive({
#Store input$file as inFile
inFile <- input$file
#if its empty return nothing
if (is.null(inFile))
return(NULL)
#read in the file as a csv, with headers, comma seperated
dd = read.csv(inFile$datapath, header = T,
sep = ",")
dd = as.data.frame(dd)
#Shortening dataframe
#dd= dd[apply(dd[, endsWith(colnames(dd), "_down")], 1, function(x) any(x == "TRUE")), ]
#Remove duplicates by ID column, and show unique
#xxx
return(dd)
})
#Make the output data_as_table a datatable containing the reactive element data
output$data_as_table<-DT::renderDataTable({
data()
})
# Downloadable csv of reactive data() object
output$downloadData <- downloadHandler(
filename = function() {
paste("Download", Sys.date(), ".csv", sep = "")
},
content = function(file) {
write.csv(data(), file, row.names = FALSE)
}
)
}
#Launch shiny app
shinyApp(ui = ui, server = server)
You can remove duplicates using dplyr::distinct. It'll only keep the first instance of the ID and remove others. In your case add this before return(dd) in data reactive -
if("ID" %in% names(dd)) {
dd <- dplyr::distinct(dd, ID, .keep_all = T)
}
Related
I have a shiny app that creates a data frame based on user inputs. I want to make a dynamic download button that takes user choices (radioButton) to download dynamically made data frame. The data frames are returned from a function as a list
When I create functions for two different download buttons the downloading works fine
library(shiny)
library(DT)
temp_func <- function(){
x <- mtcars
y = x[,1]
return(list(complete_df = as.data.frame(x), column1 = as.data.frame(y)))
}
# UI
ui <- shinyUI({
fluidPage(
actionButton("fetch", label = "Fetch data first"),
mainPanel(DT::dataTableOutput("table")),
downloadButton("down_all", "Download all"),
downloadButton("down_c1", "Download c1")
)})
# Server
server <- Main_Server <- function(input,output,session){
# Reactive Values
values <- reactiveValues(table = NULL)
# fetchEvent (Consider temp_func() is fetching data from website)
observeEvent(input$fetch, {values$table <- temp_func()})
# Rendering table for display
output$table <- renderDT({datatable(values$table$complete_df)})
# Download 1
output$down_all <- downloadHandler(
filename = function() { paste("All columns","csv", sep=".")},
content = function(file) {write.csv(values$table$complete_df, file)})
# Download 2
output$down_c1 <- downloadHandler(
filename = function() { paste("Columns1","csv", sep=".")},
content = function(file) {write.csv(values$table$column1, file)})
}
# Run-app
shinyApp(ui, server)
Once I merge the two functions and pass input$choice from radio button I get an empty file
library(shiny)
library(DT)
temp_func <- function(){
x <- mtcars
y = x[,1]
return(list(complete_df = as.data.frame(x), column1 = as.data.frame(y)))
}
# UI
ui <- shinyUI({
fluidPage(
actionButton("fetch", label = "Fetch data first"),
mainPanel(DT::dataTableOutput("table")),
radioButtons("rd", c("Select"), choices = c("All Columns" = "complete_df","Column 1" = "column1"),
selected = "complete_df"),
downloadButton("down", "Download")
)})
# Server
server <- Main_Server <- function(input,output,session){
# Reactive Values
values <- reactiveValues(table = NULL)
# fetchEvent (Consider temp_func() is fetching data from website)
observeEvent(input$fetch, {values$table <- temp_func()})
# Rendering table for display
output$table <- renderDT({datatable(values$table$complete_df)})
# Combined Download
output$down <- downloadHandler(
filename = function() { paste("File","csv", sep=".")},
content = function(file) {write.csv(values$table$input$rd, file)})
}
# Run-app
shinyApp(ui, server)
Consider temp_func() is fetching data from other website
Try to use :
# Combined Download
output$down <- downloadHandler(
filename = function() { paste("File","csv", sep=".")},
content = function(file) {write.csv(values$table[[input$rd]], file)})
The syntax you used returns NULL because values$table doesn't have an input field.
With the updated syntax, the downloaded file isn't empty anymore.
I just started to play around in shiny and made a simple app that reads a CSV file and replaces rows of one column with tokens. I would like the user to be able to download the tokenized data as a CSV file.
To do this I am using the downloadHandler() function. I have been looking in the documentation for this function, as well as similar questions in here but haven't been able to find a solution. I tried running the app externally as suggested in other similar questions.
app.R
# Only run examples in interactive R sessions
if (interactive()) {
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),
textInput(inputId = 'variable', label = 'Name of variable to pseudonymize', placeholder = 'e.g., ID_PA'),
helpText("Case sensitive!"),
downloadButton('downloadData', 'Download')
),
mainPanel(
tableOutput("contents"),
br(), br(),
tableOutput('results')
)
)
)
server <- function(input, output) {
output$contents <- renderTable({
# 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)
head(read.csv(inFile$datapath, header = input$header))
})
output$results <- renderTable({
# 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)
df <- read.csv(inFile$datapath)
# make sure to use utils::read_csv to read in data
# Function generates a lookup table that associates each unique identifier to an PSN. See lillemets
get_lookup_table <- function(data, id.var, key.length) {
if (any(duplicated(data[, id.var]))) warning('Duplicate id values in data. For longitudinal dataset, this is expected')
PSN <- c(1,1) # Allow the while loop to begin
while (any(duplicated(PSN))) { # Loop until all keys are unique
PSN <- replicate(length(unique(data[, id.var])),
paste(sample(c(LETTERS, 0:9), key.length, replace = T), collapse = ''))
}
lookup.table <- data.frame(id = unique(data[, id.var]), key = PSN)
return(lookup.table)
}
# Replace names with PSN
add_PSN <- function(data, id.var, lookup.table) {
data[, id.var] <- lookup.table[, 'key'][match(data[, id.var], lookup.table[, 'id'])]
return(data)
}
lookup_table <- get_lookup_table(df, input$variable, 10)
# Replace names with PSN
pseudo_df <- add_PSN(df, input$variable, lookup_table)
head(pseudo_df)
})
# Download file
output$downloadData <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(pseudo_df, file)
}
)
}
shinyApp(ui, server)
}
When running the app and clicking download, I get the browser error 'File not found'.
In the R console I get the warning: Error in is.data.frame: object 'pseudo_df' not found
Comments on this issue would be much appreciated.
The download handler does not know that the pseudo_df data frame was created. You probably want to have one reactive that makes the data frame and then separate render and download handlers that call the reactive that creates the data frame. So for example
make_df <- reactive({}) # code that makes the data frame goes here
output$results <- renderTable({make_df()})
output$downloadData <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(make_df(), file) # notice the call to the reactive again
}
)
I wanted to create a shiny app that gets data from user and saves it in .csv formats. But each time the user enters the data past data is not removed so when appending all the previous data(not necessarily for same user) gets appended with the new data that has been entered. I wanted to create a new "new" button which deletes past input data and gives a new table for input.
library(shiny)
ui <- fluidPage(
titlePanel("Creating a database"),
sidebarLayout(
sidebarPanel(
textInput("name", "Company Name"),
textInput("income", "Income"),
textInput("expenditure", "Expenditure"),
dateInput("date", h3("Date input"),value = Sys.Date() ,min = "0000-01-01",
max = Sys.Date(), format = "dd/mm/yy"),
actionButton("Action", "Submit"),#Submit Button
actionButton("new", "New")),
mainPanel(
tableOutput("table"), #Table showing what is there in the data frame
textInput("filename", "Enter Filename for download"), #filename
helpText(strong("Warning: Append if want to update existing data.")),
downloadButton('downloadData', 'Download'), #Button to save the file
downloadButton('Appenddata', 'Append') #Button to update a file
)
)
)
# Define server logic
server <- function(input, output){
#Global variable to save the data
Data <- data.frame()
Results <- reactive(data.frame(input$name, input$income, input$expenditure,
as.character(input$date),
as.character(Sys.Date())))
#To append the row and display in the table when the submit button is clicked
observeEvent(input$Action,{
#Append the row in the dataframe
Data <<- rbind(Data,Results())
#Display the output in the table
output$table <- renderTable(Data)
})
observeEvent(input$new, {
UpdateInputs(CreateDefaultRecord(), session)
})
output$downloadData <- downloadHandler(
# Create the download file name
filename = function() {
paste(input$filename , ".csv", sep="")
},
content = function(file) {
write.csv(Data, file,row.names = FALSE) # put Data() into the download file
})
output$Appenddata <- downloadHandler(
# Append data
filename = function() {
paste(input$filename, ".csv", sep="")
},
content = function(file) {
write.table( Data, file=file.choose(),append = T, sep=',',
row.names = FALSE, col.names = FALSE)
})
}`enter code here`
# Run the application
shinyApp(ui = ui, server = server)
The code works perfectly and i can enter new data as and when i want fine but when i press the "new" button so as to clear the data so that i want to enter new set of details the app closes down giving the following error
Warning: Error in UpdateInputs: could not find function "UpdateInputs"
The warning does not close the app until the new button is pressed. What is it that i am missing? Please help. Thank You.
If you just want to clear the data you could substitute
observeEvent(input$new, {
UpdateInputs(CreateDefaultRecord(), session)
})
with
observeEvent(input$new, {
#Append the row in the dataframe
Data <<- NULL
#Display the output in the table
output$table <- renderTable(Data)
})
although I'm not sure why you are saving Data globally using <<- and this method would obviously clear it.
I currently have a Shiny app in which you can upload a csv file and it will
show you the data.
I want to add a search bar where you can search for specific rows based on
three variables.
I then want these rows that I have searched for and then selected to form a
new downloadable csv.
Is this possible?
library(shiny)
library(shinyjs)
library(DT)
library(dplyr)
library(data.table)
#I don't need all these packages just yet but I will use them as I carry on the project hopefully
ui = fluidPage(
fileInput("Rams","Upload complete list of Rams", accept=".csv"),
#fileinput where a list of rams (male sheep) are loaded. I want to select a
few rams from the list based on their unique combinations of birth year, tag
number and flock number
br(),
DT::dataTableOutput("Rams1")
)
server = function(input, output, session) {
#server section of my app
#This shows me the data
output$Rams1 <- renderDataTable({
Rams2 <- input$Rams
if (is.null(Rams2))
return(NULL)
subset(read.csv(Rams2$datapath, header=T, sep=","))
})
}
shinyApp(ui=ui, server=server)
Even if I simply select 3 rows as done here, all 15 rows are shown in the downloaded file as shown in the next picture
The downloaded file simply produces all data rather than just the ones I am filtering for
Below code will solve the problem
library(shiny)
library(shinyjs)
library(DT)
library(dplyr)
library(data.table)
#I don't need all these packages just yet but I will use them as I carry on the project hopefully
ui = fluidPage(
fileInput("Rams","Upload complete list of Rams", accept=".csv"),
br(),
DT::dataTableOutput("Rams1"),downloadButton(outputId = "download_filtered",
label = "Download Filtered Data")
)
server = function(input, output, session) {
datasetInput <- reactive({
infile <- input$Rams
if(is.null(infile))
return(NULL)
read.csv(infile$datapath, header = TRUE)
})
output$Rams1 = DT::renderDataTable(datatable(datasetInput() ,filter = "top"),
server = FALSE)
output$download_filtered <-
downloadHandler(
filename = "Filtered Data.csv",
content = function(file){
write.csv(datasetInput()[input[["Rams1_rows_all"]], ],
file)
})
}
shinyApp(ui=ui, server=server)
I'm trying to build an application that takes a csv file from the user, uploads it, then the user fill some text boxes that will fill specific columns in the data frame later on, clicks a button 'GO', some scripts run in the background and we have a data frame ready for download. The thing is the whole reactive architecture makes it difficult to set up a step by step algorithm. Could you help me with setting up the framework for doing that? Ideally it would look like follows
shinyUI(fluidPage(
titlePanel("Uploading Files"),
fileInput('file1', 'Choose file to upload',
accept = c('text/csv',
'text/comma-separated-values',
'text/tab-separated-values',
'text/plain','.csv','.tsv')),
dateInput('date',"Select when the file was uploaded",
value = NULL,
format = 'yyyy-mm-dd'),
textInput('text1','Type what will be in column 6'),
textInput('text2','Type what will be in column 7'),
actionButton('go','go'),
tableOutput('readytable')
And now having that front i would like to: 1. Load the dataframe from csv of the user 2. Wait for the user to fill other input boxes 3. After clicking 'go' run bunch of functions on the data frame with the inputs that the user have inserted as for example df$column6 <- input$text1 and after that i'm left with a data frame that is ready to be written as a csv file once again. Thanks in advance for any links/suggestions
You can use reactive variables to control reactivity on shiny. Here is an example for your problem. Please note that the download button doesn't works on the RStudio viewer, so launch the app in a browser if you want to use the download button.
library(shiny)
runApp(list(
ui = shinyUI(pageWithSidebar(
headerPanel('Uploading Files'),
sidebarPanel(
fileInput('file1', 'Choose file to upload',
accept = c('text/csv',
'text/comma-separated-values',
'text/tab-separated-values',
'text/plain','.csv','.tsv')),
uiOutput('buttonsUI'), br(),
uiOutput('downloadUI')
),
mainPanel(
tableOutput('readytable')
)
)),
server = shinyServer(function(input, output) {
# variables to control the sequence of processes
controlVar <- reactiveValues(fileReady = FALSE, tableReady = FALSE)
# to keep the data upload
dat <- NULL
# handle the file reading
observeEvent(input$file1, {
controlVar$fileReady <- FALSE
if (is.null(input$file1))
return()
inFile <- input$file1
dat <<- read.csv(inFile$datapath)
if(!is.data.frame(dat))
return()
controlVar$fileReady <- TRUE
})
# show buttons only when file is uploaded
output$buttonsUI <- renderUI({
if (controlVar$fileReady)
div(
dateInput('date','Select when the file was uploaded',
value = NULL,
format = 'yyyy-mm-dd'),
textInput('text1','Type what will be in column 6'),
textInput('text2','Type what will be in column 7'),
actionButton('go','go')
)
})
# show a download button only if data is ready
output$downloadUI <- renderUI({
if (controlVar$tableReady)
downloadButton('downloadData', 'Download')
})
# add columns to dat and run some script on it
observeEvent(input$go, {
controlVar$tableReady <- FALSE
if (!is.null(input$text1))
dat$column6 <<- input$text1
if (!is.null(input$text2))
dat$column7 <<- input$text2
# simulate running a cool script on dat
Sys.sleep(2)
controlVar$tableReady <- TRUE
})
# render table after uploading file or running the script
output$readytable <- renderTable({
input$go
if (controlVar$fileReady || controlVar$tableReady)
dat
})
# handle the download button
output$downloadData <- downloadHandler(
filename = function() { 'newData.csv' },
content = function(file) {
write.csv(dat, file)
}
)
})
))