I created an app with shiny and shinyTable. It reads a csv file as data.frame and saves changes or new rows.
If I add a new row, it is saved but not shown in the table. I can only see the row in the table when I restart the app. How can I make sure that the submit button adds the row without restarting the app?
EDIT: I can generate this functionality with shiny and a "normal" table with renderTable, but I can't manage to get this working with shinyTable.
What I basically want to achieve is this functionality with shinyTable to have an editable table where I can add rows.
app.R
require(shiny)
datafile<-read.csv("data.csv", header=TRUE, sep=",", quote="")
runApp(
list(
ui = fluidPage(
headerPanel('Title'),
sidebarPanel(
textInput("fielda", label="fielda", value=""),
textInput("fieldb", label="fieldb", value=""),
actionButton("addButton", "insert data")
),
mainPanel(
tableOutput("table"))
),
server = function(input, output) {
datafile_sample <- datafile[sample(nrow(datafile)),]
row.names(datafile_sample) <- NULL
values <- reactiveValues()
values$df <- datafile_sample
addData <- observe({
if(input$addButton > 0) {
newLine <- isolate(c(input$fielda, input$fieldb))
isolate(values$df <- rbind(as.matrix(values$df), unlist(newLine)))
write.csv(values$df, file = "data.csv", row.names=F, quote=F)
}
})
output$table <- renderTable({values$df}, include.rownames=F)
}
)
)
data.csv
fielda,fieldb
1,2
3,4
I think I would approach this a little bit differently. As you've written your example, your data frame resulting from the read.csv shouldn't be called outside of the server. It should, itself, be reactive. In the code below, I've put it in a reactive values call, and initialized it with read.csv.
Then, when you add data to it, you can use write.table to add it to the existing file, and then update the reactive object. This should set all the pieces in motion to update automatically, regardless of what table type you use? (I'm not familiar with shinyTable, so didn't experiment with it much).
There are a few variants you can take on this. For starters, is it really necessary to write the new data to the file? Perhaps you could just append the new data to the existing data frame using rbind. (The write/read combination is going to be slow in comparison).
Even if it is necessary to write the new data, it's probably better to write the new data and use rbind to update the data frame in your app.
library(shiny)
D <- "fielda,fieldb\n1,2\n3,4"
write(D, file = "data.csv")
runApp(
list(
ui = fluidPage(
headerPanel('Title'),
sidebarPanel(
textInput("fielda", label="fielda", value=""),
textInput("fieldb", label="fieldb", value=""),
actionButton("addButton", "insert data")
),
mainPanel(
tableOutput("table"))
),
server = function(input, output) {
data <- reactiveValues(
file = read.csv("data.csv",
header=TRUE,
sep=",",
quote="")
)
addData <- observeEvent(
input$addButton,
{
newLine <- data.frame(fielda = input$fielda,
fieldb = input$fieldb)
write.table(newLine,
file = "data.csv",
col.names = FALSE,
row.names=FALSE,
quote=FALSE,
append = TRUE,
sep = ",")
data$file <- read.csv("data.csv",
header=TRUE,
sep=",",
quote="")
}
)
output$table <-
renderTable(data$file, include.rownames=FALSE)
}
)
)
Related
I'm working on a shiny app to manipulate data.
I'd like to read a zip file selectioned in a fileInput. This zip is composed by multiple csv files, and I'd like to save as reactive values all .csv dataframes.
For example, if test.zip contains file ONE.csv, TWO.csv, THREE.csv , i'd like to obtain 3 reactives values (as dataframes) called ONE , TWO, THREE .
I'm abble to do it if I know the name and number of csv files.
But if I don't know the number and names of .csv dataframes, how can I achieve it ?
## Only run examples in interactive R sessions
if (interactive()) {
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("ZIP", "Choose ZIP File",
accept = ".zip"
)
),
mainPanel(
DT::dataTableOutput("ONEtab")
)
)
)
server <- function(input, output) {
ONE <- reactive({
inFile <-req(input$ZIP)
read_csv(unzip(inFile$datapath,"ONE.CSV"))
})
TWO <- reactive({
inFile <-req(input$ZIP)
read_csv(unzip(inFile$datapath,"TWO.CSV"))
})
THREE <- reactive({
inFile <-req(input$ZIP)
read_csv(unzip(inFile$datapath,"THREE.CSV"))
})
output$ONEtab <- DT::renderDataTable({ DT::datatable(ONE(), option=list(scrollX=T),filter = 'top')})
}
shinyApp(ui, server)
}
Thanks for your help !
One option is to read all the dataframes into a single variable and then use a number to select the one of interest. Here's some code that does this. It uses lapply to read the contents of the zip file to create a reactive variable called all. To reference different dataframes, the code required is all()[[index]] and I have added something that shows this.
library(DT)
library(readr)
ui <- fluidPage(sidebarLayout(sidebarPanel(
fileInput("ZIP", "Choose ZIP File", accept = ".zip"),
selectInput("choice", 'Choose', choices = c(1,2,3), selected = 1)
),
mainPanel(DT::dataTableOutput("selectone"))))
server <- function(input, output) {
all <- reactive({
inFile <- req(input$ZIP)
filelist <- unzip(inFile$datapath, list = T)
lapply(filelist$Name, read_csv)
})
output$selectone <-
DT::renderDataTable({
choice = as.integer(input$choice)
DT::datatable(all()[[choice]], option = list(scrollX = T), filter = 'top')
})
}
shinyApp(ui, server)
Without the rest of your code that processes this, it's difficult to know if this will be what you need but perhaps it's a start.
I am new to shiny and trying to combine a couple features and having some trouble.
I want for the user to be able to select a CSV and then be presented with a random instance (in this case tweet) from that table. The following code worked when "tweetData" was a statically loaded csv using read_csv.
## function to return random row number from data set
getTweet <- function(){
tweetData[sample(nrow(tweetData), 1), ]
}
function(input, output, session) {
## set reactive values, get randomized tweet
appVals <- reactiveValues(
tweet = getTweet(),
ratings = data.frame(tweet = character(), screen_name = character(), rating = character())
)
I want to instead use a dynamically chosen csv for "tweetData", something like adding this??
csvName <- reactive(paste0('../folder_path/', input$file_name))
selectedData <- read.csv(csvName)
How can use reactively chosen csvs to fit into the structure of the first code chunk?
You might be looking for fileInput for giving user an option to upload a dataset.
This is a simple reproducible example -
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File"),
),
mainPanel(
tableOutput("contents")
)
)
)
server <- function(input, output) {
output$contents <- renderTable({
req(input$file1)
read.csv(input$file1$datapath)
})
}
shinyApp(ui, server)
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)
}
If I import a dataset from Shiny using fileInput, how could I go about creating this in a reactive form where I can create subsets of the imported dataframe and eventually perform calculations on different rows of the subsetted dataframes? Can I store subsetted dataframes as reactiveValues() and then use them outside of a reactive scenario?
How would I go about accomplishing something like the code below, which would be in an ordinary R Script, where it works successfully?
df <- read.table(file.choose(), header=TRUE, sep=",")
attach(df)
df <- df[, c(1, 50:75)]
df[1] <- time
I know I can accomplish the following using fileInput, I'm just not sure how I can subset things like this within shiny and make them usable in scenarios like
renderPlot and others. Would reactive or reactiveValues be the best strategy to accomplish this?
Is this something you are looking for?
library(shiny)
ui <- fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(sidebarPanel(
fileInput(
"file1",
"Choose CSV File",
multiple = F,
accept = c("text/csv", "text/comma-separated-values,text/plain", "text")
),
uiOutput("selectbox")
),
mainPanel(tableOutput("contents")))
)
server <- function(input, output) {
data <- reactive({
req(input$file1)
df <- read.csv(file = input$file1$datapath,
header = T,
sep = "\t")
})
output$selectbox <- renderUI({
colnam <- colnames(data())
selectInput("colsel",
"Columns Selected",
c("Please select" = "", colnam),
multiple = T)
})
output$contents <- renderTable({
data()[, c(req(input$colsel))]
})
}
shinyApp(ui, server)
Instead of specifying separate fileInput variables, I'd like to use reactiveValues to store uploaded CSV dataframes, manipulate them in some way, and then store them for accession later. My design is to name each dataframe by its filename and append to the reactiveValue rvTL. My questions are,
How can I access individual dataframes under the list I created using reactiveValuesToList(rvTL)?
Next step, how to create a selectInput menu to access the individual dataframes uploaded by fileInput
To learn this concept, I am piggybacking off the answer from Dean Attali and made rvTL the same as his values variable.
R shiny: How to get an reactive data frame updated each time pressing an actionButton without creating a new reactive data frame?
I've gone over many example codes on reactiveValues, yet still at an incomplete understanding. Most examples are using some sort variation on reactiveValuesToList(input) R Shiny: Keep/retain values of reactive inputs after modifying selection, I'm really not seeing the logic here. Any help/suggestions would be appreciated!
library(shiny)
runApp(shinyApp(
ui=(fluidPage(
titlePanel("amend data frame"),
mainPanel(
fileInput("file", "Upload file", multiple=T),
tabsetPanel(type="tabs",
tabPanel("tab1",
numericInput("Delete", "Delete row:", 1, step = 1),
actionButton("Go", "Delete!"),
verbatimTextOutput("df_data_files"),
verbatimTextOutput("values"),
verbatimTextOutput("rvTL"),
tableOutput("rvTL_out")
),
tabPanel("tab2",
tableOutput("df_data_out")
)
)))),
server = (function(input, output) {
values <- reactiveValues(df_data = NULL) ##reactiveValues
rvTL <- reactiveValues(rvTL = NULL)
observeEvent(input$file, {
values$df_data <- read.csv(input$file$datapath)
rvTL[[input$file$name]] <- c(isolate(rvTL), read.csv(input$file$datapath))
})
observeEvent(input$Go, {
temp <- values$df_data[-input$Delete, ]
values$df_data <- temp
})
output$df_data_files <- renderPrint(input$file$name)
output$values <- renderPrint(names(values))
output$rvTL <- renderPrint(names(reactiveValuesToList(rvTL))[1] )
output$rvTL_out <- renderTable(reactiveValuesToList(rvTL)[[1]])
output$df_data_out <- renderTable(values$df_data)
})
))
It really is as straightforward as you thought. You were close too, just fell into some syntax traps. I made the following changes:
that c(isolate(.. call was messing things up, I got rid of it. It was leading to those "Warning: Error in as.data.frame.default: cannot coerce class "c("ReactiveValues", "R6")" to a data.frame" errors.
Also you were reusing the rvTL name too often which is confusing and can lead to conflicts, so I renamed a couple of them.
I also added a loaded file name list (lfnamelist) to keep track of what was loaded. I could have used names(rvTL$dflist) for this but it didn't occur to me at the time - and I also this is a useful example of how to organize related reactive values into one declaration.
And then I added rendered selectInput so you can inspect what is saved in the reactiveValue list.
So here is the adjusted code:
library(shiny)
runApp(shinyApp(
ui=(fluidPage(
titlePanel("amend data frame"),
mainPanel(
fileInput("file", "Upload file", multiple=T),
tabsetPanel(type="tabs",
tabPanel("rvTL tab",
numericInput("Delete", "Delete row:", 1, step = 1),
uiOutput("filesloaded"),
actionButton("Go", "Delete!"),
verbatimTextOutput("df_data_files"),
verbatimTextOutput("values"),
verbatimTextOutput("rvTL_names"),
tableOutput("rvTL_out")
),
tabPanel("values tab",
tableOutput("df_data_out")
)
)))),
server = (function(input, output) {
values <- reactiveValues(df_data = NULL) ##reactiveValues
rvTL <- reactiveValues(dflist=NULL,lfnamelist=NULL)
observeEvent(input$file, {
req(input$file)
values$df_data <- read.csv(input$file$datapath)
rvTL$dflist[[input$file$name]] <-read.csv(input$file$datapath)
rvTL$lfnamelist <- c( rvTL$lfnamelist, input$file$name )
})
observeEvent(input$Go, {
temp <- values$df_data[-input$Delete, ]
values$df_data <- temp
})
output$df_data_files <- renderPrint(input$file$name)
output$values <- renderPrint(names(values))
output$rvTL_names <- renderPrint(names(rvTL$dflist))
output$rvTL_out <- renderTable(rvTL$dflist[[input$lftoshow]])
output$df_data_out <- renderTable(values$df_data)
output$filesloaded <- renderUI(selectInput("lftoshow","File to show",choices=rvTL$lfnamelist))
})
))
And here is a screen shot: