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.
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.
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)
}
This question already has an answer here:
R shiny table with dates
(1 answer)
Closed 4 years ago.
While creating a shiny app to get a data frame according to user input, i use the following, which works perfectly fine:
library(shiny)
ui <- fluidPage(
textInput("name", "Comnnay 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"),
#Table showing what is there in the data frame
tableOutput("table"),
#Button which appends row to the existing dataframe
actionButton("Action", "Submit"),
#Button to save the file
downloadButton('downloadData', 'Download')
)
library(shiny)
server <- function(input, output){
#Global variable to save the data
Data <- data.frame()
Results <- reactive(data.frame(input$name, input$income, input$expenditure,
input$date , 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)
})
output$downloadData <- downloadHandler(
# Create the download file name
filename = function() {
paste("data-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(Data, file) # put Data() into the download file
})
}
shinyApp(ui = ui, server = server)
But when i press the submit button it does not print the date in the format i want
What do i do to change the output version. Or do i have to specify something specially? Thank you.
All dates in most languages are numeric, which is expected. If you want the string simply parse into it
Results <- reactive(data.frame(input$name, input$income, input$expenditure,
as.character(input$date), as.character(Sys.Date())))
I am building a rudimentary shiny app.
First, I created a data frame 'x' and saved it in my working directory:
x <- data.frame(a = 1:4, b = 2:5)
write.csv(x, 'x.csv', row.names = F)
In my shiny I'd like to:
Upload file 'x.csv'
Click my action button 'Click Here' and run a few commands upon clicking it.
Get a message printed in the Shiny app itself: "Load a file!" if I click on my button "Click here" after forgetting to upload the file first.
My code works, but I can't figure out how to make my message appear.
My code:
library(shiny)
ui <- fluidPage(
br(),
# User should upload file x here:
fileInput("file_x", label = h5("Upload file 'x'!")),
br(),
# Users clicks the button:
actionButton("do_it", "Click Here"),
br(),
# Print last value of the button 'do_it':
verbatimTextOutput("print_action")
)
server <- function(input, output, session) {
observeEvent(input$do_it, {
# Just a check of my button's actions:
output$print_action <- renderPrint({input$do_it})
# Validating the input - next 5 lines are not working:
# validate(
# need(
# try(is.null(input$file_x), "Load a file!")
# )
# )
# Reading in the file:
fileData <- reactive({
infile <- input$file_x
if (is.null(infile)) {
return(NULL)
}
read.csv(infile$datapath)
})
x <- fileData()
# Writing out the same file - but under a different name:
filename <- paste0("x", input$do_it, ".csv")
write.csv(x, file = filename, row.names = FALSE)
})
}
shinyApp(ui, server)
I think rather than displaying text, maybe modalDialog is better suited for what you are trying to achieve. I have implemented both solutions below, so you can compare.
Note that I also modified the reading of the csv slightly. It is bad practice to set a reactive from inside an observer. In those cases, it is better to use a reactiveVal, and update that from an observer.
Hope this helps!
library(shiny)
ui <- fluidPage(
br(),
# User should upload file x here:
fileInput("file_x", label = h5("Upload file 'x'!")),
br(),
# Users clicks the button:
actionButton("do_it", "Click Here"),
br(),
br(),
# Print last value of the button 'do_it':
verbatimTextOutput("print_action")
)
server <- function(input, output, session) {
observeEvent(input$do_it, {
if(is.null(input$file_x))
{
# show pop-up ...
showModal(modalDialog(
title = "Oh no!",
paste0("You have not uploaded a file, silly person!"),
easyClose = TRUE,
footer = NULL
))
# ... or update the text
my_text('Please upload a file.')
}
else
{
# Reading in the file:
infile <- input$file_x
if (is.null(infile)) {
return(NULL)
}
x <- read.csv(infile$datapath)
fileData(x) # set the reactiveVal called fileData to the file inputs.
# Writing out the same file - but under a different name:
filename <- paste0("x", input$do_it, ".csv")
write.csv(x, file = filename, row.names = FALSE)
my_text('Succes!')
}
})
fileData <- reactiveVal()
my_text <- reactiveVal('')
output$print_action <- renderText({my_text()})
}
shinyApp(ui, server)
Here are the requirements.
1)I need to browse and upload an excel file (with package readxl), which is used to arrive at some calculations which I need to display in shiny window as a different table output
2) Manually edit some data in the uploaded file and it should automatically reflect in the results displayed
3) We should be able to download the edited file.
I have written so far. I have columns ID, exposure and frequency in input data. For each ID I need to calculate a variable using corresponding exposure and frequency which would be displayed. I need to manually edit frequency and Exposure using ID ( which is unique ). I have added an "update" button. But change is not permanent. It goes back as soon as I click update button once more
library(shiny)
ui = fluidPage(
titlePanel("HEllo world"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose xlsx file',
accept = c(".xlsx")),
actionButton("go", "update"),
numericInput('NewVal', 'Enter new Frequency',NULL),
numericInput('NewExp', 'Enter new Exposure',NULL)),
mainPanel(
textInput('ID', 'Enter ID'),
dataTableOutput('contents')
)))
server = function(input,output){
ef <- eventReactive(input$go, {
infile <- input$file1
if(is.null(infile))
return(NULL)
file.rename(infile$datapath,paste(infile$datapath, ".xlsx", sep=""))
data<-read_excel(paste(infile$datapath, ".xlsx", sep=""), 1)
if(input$ID!="" && input$go>0){
for( i in 1:nrow(data)){
if( input$ID == data$'ID'[i]){
if(!is.na(input$NewVal)){
data$' Frequency'[i] <- input$NewVal
}
if(!is.na(input$NewExp)){
data$'Exposure'[i] <- input$NewExp
}
}}}
data
}, ignoreNULL = FALSE)
output$contents <- renderDataTable({ef()})}
shinyApp(ui,server)
UPDATE!:As per one answer, I have made some changes to my code. The new code seems to be working fine. Here is the working code, for anyone who might need help with the same issue.
ui = fluidPage(
titlePanel("HEllo world"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose xlsx file',
accept = c(".xlsx")),
actionButton("go", "update"),
numericInput('NewVal', 'Enter new Frequency',NULL),
numericInput('NewExp', 'Enter new Exposure',NULL)),
mainPanel(
textInput('ID', 'Enter ID'),
tableOutput('contents')
)))
server = function(input,output){
# Reactive value to save input data frame for use elsewhere
original <- reactiveValues()
observeEvent(input$file1, {
theFile <- input$file1
if(is.null(theFile)) {
return(NULL)}
**file.rename(theFile$datapath,paste(theFile$datapath, ".xlsx", sep=""))**
original$oldData <- read_excel(paste(theFile$datapath, ".xlsx", sep = ""), 1)
})
observeEvent(input$go, {
original$newData <- original$oldData
if(input$ID !="") {
for( i in 1:nrow(original$oldData)){
if( input$ID == original$oldData$'ID'[i]){
if(!is.na(input$NewVal)){
original$newData$'Frequency'[i] <- input$NewVal
}
if(!is.na(input$NewExp)){
original$newData$'Exposure'[i] <- input$NewExp
}
}
}
**original$oldData<-original$newData** }
})
output$contents <- renderTable({
if(!is.null(original$newData)) {
original$newData}
else {
original$oldData}
})
}
shinyApp(ui = ui, server = server)
Some of the comments seem to be on the right track of what's going on here. There's several solutions that could be used, but I'll just share what's most intuitive to me. Also, I'll only be changing the server function.
server = function(input,output){
# Reactive value to save input data frame for use elsewhere
original <- reactiveValues()
observeEvent(input$file1, {
theFile <- input$file1
if(is.null(theFile)) {return(NULL)}
original$oldData <- read_excel(paste(theFile$datapath, ".xlsx", sep = ""), 1)
})
observeEvent(input$goButton2, {
original$newData <- original$oldData
if(input$ID !="") {
for( i in 1:nrow(data)){
if( input$ID == dat$'ID'[i]){
if(!is.na(input$NewVal)){
original$newData$' Frequency'[i] <- input$NewVal
}
if(!is.na(input$NewExp)){
original$newData$'Exposure'[i] <- input$NewExp
}
}
}
}
})
output$contents <- renderDataTable({
if(!is.null(original$newData)) {original$newData}
else {original$oldData}
})
}
This won't change the table output until the go button is clicked. I haven't tested it fully, since I don't have your data, but I believe this should set you on the right track at the bare minimum... I like observe statements, because they cause side effects and seem more open ended than eventReactives or functions.
This only helps with the initial issues of having the correct changes made and continued showing in the output. If this works, it should be fairly easy to add a download function, which saves the file whenever it's updated.
Update 1
The code below should do what you would want it to do. I've added two different capabilities for saving the new data frame. The commented out code saves the data automatically whenever the update button is pressed. The code that's there without comments around it creates a download button for downloading the data. I've also added a line that calculates a new value based off frequency and exposure. Named this column Value in data set. Hope this helps!
#### Example app for Exchange answer
library(shiny)
library(readxl)
ui = fluidPage(
titlePanel("HEllo world"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose xlsx file',
accept = c(".xlsx")),
actionButton("go", "update"),
numericInput('NewVal', 'Enter new Frequency',NULL),
numericInput('NewExp', 'Enter new Exposure',NULL),
# Download button (goes with download handler below)
# Use if desire is to save at press of button
downloadButton("save", "Download")
),
mainPanel(
textInput('ID', 'Enter ID'),
dataTableOutput('contents')
)
)
)
server = function(input,output){
# Reactive value to save input data frame for use elsewhere
original <- reactiveValues()
observeEvent(input$file1, {
theFile <- input$file1
if(is.null(theFile)) {
original$oldData <- NULL
} else {
original$oldData <- read_excel(theFile$datapath, 1)
}
})
observeEvent(input$go, {
original$newData <- original$oldData
if(input$ID !="") {
for(i in 1:nrow(original$oldData)){
if(input$ID == original$oldData$'ID'[i]){
if(!is.na(input$NewVal)){
original$newData$'Frequency'[i] <- input$NewVal
}
if(!is.na(input$NewExp)){
original$newData$'Exposure'[i] <- input$NewExp
}
### Make sure a column in your data set is named Value for this
# Calculate a new column
original$newData$'Value'[i] <- (original$newData$'Exposure'[i]*
original$newData$'Frequency'[i])
}
}
original$oldData<-original$newData
}
### Use this to automatically save table when update is clicked
# write.csv(original$newData,
# file = #Desired Pathname,
# row.names = FALSE)
})
output$contents <- renderDataTable({
if(!is.null(original$newData)) {
original$newData}
else {
original$oldData
}
})
### Use this code below if desired saving is through download button
# Server code for download button
output$save <- downloadHandler(
filename = function() {
paste0("newData - ", Sys.Date(), ".csv")
},
content = function(con) {
if (!is.null(original$newData)) {
dataSave <- original$newData
} else {
dataSave <- original$oldData
}
con <- ## Desired save location... could just use `getwd()` to
# save to working directory
write.csv(dataSave, con)
}
)
}
shinyApp(ui = ui, server = server)