Download a table created in Shiny - r

I need to give users a set of 60 observations. I have a master table that I want to to subset these 60 observations from. So, (1) I host the master table as a published csv file on google drive. (2) Write a shiny code to subset 60 values in R studio. The user will have to enter a group ID that I use as set.seed and ensure that the user sees the same subset every time he / she attempts to get the 60 observations. And, it also helps me keep track of the observations that the user has.
The code works fine and I am able to show the subset table. But, I am not able to get the download to work. I saw a post that says renderTable create an HTML table that cannot be downloaded and I should create the table outside it. I tried using reactive to do this, but it did not work and kept giving various errors. For example:
"cannot coerce class ‘c("reactiveExpr", "reactive", "function")’ to a data.frame"
Will appreciate any help of this - even if someone can please point out to what I should read and try to make this work.
library(shiny)
db1 <- read.csv("https://docs.google.com/spreadsheets/d/e/2PACX-1vS94xYLix6bDUNNXAgHejdQ-CcWi-G4t25nfxuhRZF57TloC8NwVgnperBB9-U-IuDvMcOnvdc9iavU/pub?gid=0&single=true&output=csv")
# Define UI
ui <- fluidPage(
# Application title
titlePanel("MnM"),
# Sidebar to take input of group ID
sidebarLayout(
sidebarPanel(
numericInput("seed","Group ID:", value = 100, min = 100, max = 999),
downloadButton("downloadData", "Download")
),
# Show the table
mainPanel(
tableOutput("table")
)
)
)
# Define server logic for the table
server <- function(input, output) {
output$table <- renderTable({
set.seed(input$seed)
zz <- sample(1:nrow(db1), size = 60, replace = TRUE)
data.frame(db1[zz,])})
output$downloadData <- downloadHandler(
filename = "test.csv",
content = function(file) {
write.csv(output$table, file, row.names = FALSE)
}
)
}
# Run the application
shinyApp(ui = ui, server = server)

Create your table once, and then use it in your renderTable and downloadHandler. Create it as a reactive, so its available everywhere.
Note that downloadHandler doesn't work in RStudio's preview, view it in a browser instead. There is a button labelled 'Open in Browser' that will do this.
Here is your code with that applied:
library(shiny)
db1 <- read.csv("https://docs.google.com/spreadsheets/d/e/2PACX-1vS94xYLix6bDUNNXAgHejdQ-CcWi-G4t25nfxuhRZF57TloC8NwVgnperBB9-U-IuDvMcOnvdc9iavU/pub?gid=0&single=true&output=csv")
# Define UI
ui <- fluidPage(
# Application title
titlePanel("MnM"),
# Sidebar to take input of group ID
sidebarLayout(
sidebarPanel(
numericInput("seed","Group ID:", value = 100, min = 100, max = 999),
downloadButton("downloadData", "Download")
),
# Show the table
mainPanel(
tableOutput("table")
)
)
)
# Define server logic for the table
server <- function(input, output) {
#Create dataframe
mytable <- reactive({
set.seed(input$seed)
zz <- sample(1:nrow(db1), size = 60, replace = TRUE)
data.frame(db1[zz,])
})
#Display dataframe in table
output$table <- renderTable({
mytable()
})
#Download dataframe
output$downloadData <- downloadHandler(
filename = "test.csv",
content = function(file) {
write.csv(mytable(), file, row.names = FALSE)
}
)
}
# Run the application
shinyApp(ui = ui, server = server)

Related

How to create a Shiny-app that selects from multiple dfs, edits one and downloads edited data

I try to create a shiny app in which one can choose from different dfs. Then one can edit values in the table. At last I would like to download the edited table.
Each step for itself , edit and download, select and download is no problem. All three together: great despair.
I don't seem to understand how Shiny updates the reactive values and how you can cut it off with isolate.
library(shiny)
library(DT)
ui <- fluidPage(
# App title ----
titlePanel("Downloading Data"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Choose dataset ----
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars"), multiple=T),
actionButton(inputId = "goButton",
label = "Run Report"),
# downloadbutton
downloadButton("downloadData", "Download")
),
# Main panel for displaying outputs ----
mainPanel(
DTOutput("x1")
)
)
)
#df <- cars #if this is taken instead of the first "eventReactive" it works
server <- function(input, output) {
eventReactive({
# Take a dependency on input$goButton
input$goButton
# Use isolate() to avoid dependency on input$obs
df <- isolate(input$dataset)
})
#render the editable DT
output[["x1"]] <- renderDT({
datatable(
df,
selection = "single",
editable = TRUE
)
})
# Creating a DF with the edited info
aniRoi2 <- reactiveVal(df)
#Creating proxy
proxy <- dataTableProxy("x1")
#storing edited df in proxy
observeEvent(input[["x1_cell_edit"]], {
info <- input[["x1_cell_edit"]]
newAniroi2 <-
editData(aniRoi2(), info, proxy, rownames = TRUE, resetPaging = FALSE)
aniRoi2(newAniroi2)
saveRDS(newAniroi2, "data_entry_form.rds") # save rds
})
#download the proxy
output$downloadData <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(aniRoi2(), file)
}
)
}
shinyApp(ui, server)
Here I try to select a dataset, that only gets loaded by press of a button. Then it should behave like a normal data.frame or tibble.
If I take out the possibilty of selection of dataframes and call between "ui" and "server" "df <- cars" then it works as intended.
As of now I get the error message:
Listening on http://127.0.0.1:4060
Warnung: Error in is_quosure: Argument "expr" fehlt (ohne Standardwert)
52: is_quosure
51: exprToQuo
50: eventReactive
49: server [#2]
3: runApp
2: print.shiny.appobj
1:
Error in is_quosure(expr) : Argument "expr" fehlt (ohne Standardwert)
Thank you very much, any help would be much appreciated. It feels as if I am so close (but it feels like that since a week).
I also tried this download edited data table gives warning in shiny app it uses observe to wrap the selection. But in the shiny-app I get the familiar "Error 'data' must be 2-dimensional (e.g. data frame or matrix)"
PS: If you would forgive a bonus question: How do you debug shiny? I mean how can you see inside of what is happening, how the environment looks like and which processes are working?
There are some issues with your code. First, input$dataset is a character string with the name of the chosen dataset, not the dataset itself. To get the dataset use e.g. get(input$dataset). Second, the way you use eventReactive is interesting. (; Overall I would go for an observeEvent to init your reactiveVal aniRoi2 with the chosen dataset. Finally I have set multiple=FALSE in your selectInput as choosing multiple df's breaks your code and allowing the user to select multiple dfs makes no sense to me. (I guess that you have done this to get rid of the pre-selected value?? )
library(shiny)
library(DT)
ui <- fluidPage(
titlePanel("Downloading Data"),
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars"), multiple = FALSE
),
actionButton(
inputId = "goButton",
label = "Run Report"
),
downloadButton("downloadData", "Download")
),
mainPanel(
DTOutput("x1")
)
)
)
server <- function(input, output) {
aniRoi2 <- reactiveVal()
observeEvent(input$goButton, {
aniRoi2(get(input$dataset))
})
output[["x1"]] <- renderDT({
datatable(
aniRoi2(),
selection = "single",
editable = TRUE
)
})
proxy <- dataTableProxy("x1")
observeEvent(input[["x1_cell_edit"]], {
info <- input[["x1_cell_edit"]]
newAniroi2 <-
editData(aniRoi2(), info, proxy, rownames = TRUE, resetPaging = FALSE)
aniRoi2(newAniroi2)
})
output$downloadData <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(aniRoi2(), file)
}
)
}
shinyApp(ui, server)

Reactive CSV selection then used in function as df

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)

Dataframe - R - Shiny

i have a question regarding Shiny and the usage of Data frames.
I think i understood that i need to create isolated or reactive environmentes to interact with, but if i try to work with the Dataframe i get an error message:
Error in pfData: konnte Funktion "pfData" nicht finden
i tried to manipulate the dataframe by this code:
server <- function(input, output) {
observeEvent(input$go,
{
pf_name <- reactive({input$pfID})
pf_date <- reactive({input$pfDate})
if (pf_name()!="please select a PF") {
pfData <- reactive(read.csv(file =paste(pf_name(),".csv",sep=""),sep=";",dec=","))
MDur <- pfData()[1,15]
pfData <- pfData()[3:nrow(pfData()),]
Total = sum(pfData()$Eco.Exp...Value.long)
}
})
}
If i manipulate my Dataframe in the console it works just fine:
pfData <- pfData[3:nrow(pfData),]
Total = sum(pfData$Eco.Exp...Value.long)
Assets = sum(as.numeric(gsub(",",".",gsub(".","",pfData$Value,fixed=TRUE),fixed=TRUE)))
pfData$Exposure <- with(pfData, Eco.Exp...Value.long /Total)
can you help me?
Edit:
library(shiny)
ui <- fluidPage(
fluidRow(
column(6, offset =3,
wellPanel(
"Choose Mandate and Date",
fluidRow(
column(4,selectInput("pfID",label = "",
choices = list("please select a PF","GF25",
"FPM"),
selected = "please select a PF") ),
column(4, dateInput("pfDate",label="",value = Sys.Date()) ),
column(2, actionButton("go","Submit")),column(2,textOutput("selected_var"))
)
)
)
)
)
# Define server logic ----
server <- function(input, output) {
pfDataReactive <- reactive({
input$go
if (pf_name()!="please select a PF") {
pfData <- read.csv(file =paste(pf_name(),".csv",sep=""),sep=";",dec=",")
MDur <- pfData[1,15]
pfData <- pfData[3:nrow(pfData),]
Total = sum(pfData$Eco.Exp...Value.long)
Assets = sum(as.numeric(gsub(",",".",gsub(".","",pfData$Value,fixed=TRUE),fixed=TRUE)))
pfData$Exposure <- with(pfData, Eco.Exp...Value.long /Total)
pfData
output$selected_var <- renderText({paste(MDur)})
}
})
}
# Run the app ----
shinyApp(ui = ui, server = server)
Thank you
Stefan
Without a working example, it's imposible to be sure what you're trying to do, but it sounds like you need a reactive rather than using observeEvent.
Try something like
pfDataReactive <- reactive({
input$go
pfData <- read.csv(file =paste(pf_name(),".csv",sep=""),sep=";",dec=",")
Total = sum(pfData$Eco.Exp...Value.long)
Assets = sum(as.numeric(gsub(",",".",gsub(".","",pfData$Value,fixed=TRUE),fixed=TRUE)))
pfData$Exposure <- with(pfData, Eco.Exp...Value.long /Total)
pfData
})
And then use pfDataReactive() in your Shiny app's server function wherever you would refer to pfData in your console code.
The standalone reference to input$go ensures the reactive will update whenever input$go changes/is clicked/etc.
Update
There are still significant issues with your code. You've added an assignment to an output object as the last line of the reactive I gave you, so the reactive always returns NULL. That's not helpful and is one of the reasons why it "doesn't active at all"...
Second, you test for the existence of an reactive/function called pf_name when the relevant input object appears to be input$pfID. That's another reason why the reactive is never updated.
Note the change to the definition of input$pfID that I've made to improve the readability of the pfDataReactive object. (This change also probably means that you can do away with input$go entirely.)
As you say, I don't have access to your csv file, so I can't test your code completely. I've modified the body of the pfDataReactive to simply return the mtcars dataset as a string. I've also edited the code I've commented out to hopefully run correctly when you use it with the real csv file.
This code appears to give the behaviour you want,. Though, if I may make a subjective comment, I think the layout of your GUI is appaling. ;=)
library(shiny)
ui <- fluidPage(
fluidRow(
column(6, offset =3,
wellPanel(
"Choose Mandate and Date",
fluidRow(
column(4,selectInput("pfID",label = "",
# Modified to that "Pleaseselect a PF" returns NULL
choices = list("please select a PF"="","GF25", "FPM"),
selected = "please select a PF") ),
column(4, dateInput("pfDate",label="",value = Sys.Date()) ),
column(2, actionButton("go","Submit")),column(2,textOutput("selected_var"))
)
)
)
)
)
# Define server logic ----
server <- function(input, output) {
pfDataReactive <- reactive({
# Don't do anything until we have a PF csv file
req(input$pfID)
input$go
# Note the change to the creation of the file name
# pfData <- read.csv(file =paste(input$pfID,".csv",sep=""),sep=";",dec=",")
# pfData <- pfData[3:nrow(pfData),]
# Total = sum(pfData$Eco.Exp...Value.long)
# Assets = sum(as.numeric(gsub(",",".",gsub(".","",pfData$Value,fixed=TRUE),fixed=TRUE)))
# pfData$Exposure <- with(pfData, Eco.Exp...Value.long /Total)
# MDur <- pfData[1,15]
# If you want to print MDur in the selected_var output, MDur should be the retrun value from this reactive
# MDur
mtcars
})
output$selected_var <- renderText({
print("Yep!")
as.character(pfDataReactive())
})
}
# Run the app ----
shinyApp(ui = ui, server = server)
Next time, please, please, make more effort to provide a MWE. This post may help.
This is a good introduction to Shiny.

Use Shiny to collect data from users in Google Sheet

I would like to use a Shiny interface to collect data from user inputs, such as in this Medium Article
The article is written for the googlesheets package, but we now need to use googlesheets4.
I think my code will not work due to may lay of understanding of reactive elements.
#load libraries
library(shiny)
library(shinydashboard)
library(googlesheets4)
library(DT)
ui <- fluidPage(
# Define UI
ui <- fluidPage(
# App title ----
titlePanel("Seflie Feedback"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar to demonstrate various slider options ----
sidebarPanel(
# Input: Overall Rating
sliderInput(inputId = "helpful",
label = "I think this app is helpful",
min = 1,
max = 7,
value = 3),
actionButton("submit", "Submit")
),
mainPanel(
))
)
)
server <- function(input, output, session) {
# Reactive expression to create data frame of all input values ----
sliderValues <- reactive({
usefulRating <- input$helpful
Data <- data.frame(
Value = as.character(usefulRating),
stringsAsFactors = FALSE)
})
#This will add the new row at the bottom of the dataset in Google Sheets.
observeEvent(input$submit, {
MySheet <- gs4_find() #Obtain the id for the target Sheet
MySheet <- gs4_get('https://docs.google.com/spreadsheets/d/162KTHgd3GngqjTm7Ya9AYz4_r3cyntDc7AtfhPCNHVE/edit?usp=sharing')
sheet_append(MySheet , data = Data)
})
}
shinyApp(ui = ui, server = server)
I replaced the gs4_get() with the link rather than the ID to support you in helping me. If you are not able to access the link, you can replace the link with a google sheet ID from your own sheets temporarily.
When I run the code, I see the following: Warning: Error in is.data.frame: object 'Data' not found.
When I replace the usefulRating <- input$helpful with usefulRating <- 4 or usefulRating <- 5 or some other value, the data writes to the Sheet.
Thanks for any insights :)
#load libraries
library(shiny)
library(shinydashboard)
library(googlesheets4)
library(DT)
ui <- fluidPage(
titlePanel("Seflie Feedback"),
sidebarLayout(
sidebarPanel(
#This is where a user could type feedback
textInput("feedback", "Plesae submit your feedback"),
),
#This for a user to submit the feeback they have typed
actionButton("submit", "Submit")),
mainPanel())
server <- function(input, output, session) {
textB <- reactive({
as.data.frame(input$feedback)
})
observeEvent(input$submit, {
Selfie <- gs4_get('https://docs.google.com/spreadsheets/d/162KTHgd3GngqjTm7Ya9AYz4_r3cyntDc7AtfhPCNHVE/edit?usp=sharing')
sheet_append(Selfie, data = textB())
})
}
shinyApp(ui = ui, server = server)

How to update DT datatable in Shiny when within a module and the selection criteria are changed

I try to make a shiny module to present data from dataframes using the DT package. I would like to use a module to have a standard set up of DT-table options like language and others.
I want the user to be able to select different subsets of the data interactively and thereafter be able to see the data as a DT-table. The selection of the subset will be generated outside the module because I would like the subset to be available for other uses, for example to be exported to a csv-file.
This works as intended when I don't use a module for making the DT table. When I put the code inside a module, a table is produced when the app starts. But when the selection criteria are changed, the table don't update.
I have included an app illustrating the problem. Table 1 is generated without using shiny module and updates as expected when the selection changes. Table 2 is output using the module and don't update when the selection is changed.
I'm running R-studio 1.1.463, R version 3.5.2 and DT version 0.5.
require("DT")
require("shiny")
# module for presenting data using DT
showDTdataUI <- function(id) {
ns <- NS(id)
tagList(
DT::dataTableOutput(ns("table"))
)
}
showDTdata <- function(input, output, session, DTdata) {
output$table <- renderDataTable({
DT::datatable(DTdata)
})
}
# User interface
ui <-
fluidPage(
sidebarLayout(
sidebarPanel(id="DT",
width = 4,
helpText(h4("Select")),
selectInput("selectedSpecies", label = "Species",
choices = c("setosa","versicolor","virginica"),
selected = "versicolor")
),
mainPanel(
h3("Table 1. Presenting selected data from Iris" ),
DT::dataTableOutput("table"),
h5(br("")),
h3("Table 2. Presenting selected data from Iris using shiny module"),
showDTdataUI(id="testDTModule")
)
)
)
# Define server logic ----
server <- function(session, input, output) {
selectedIris <- reactive ( {
selected <- iris[which(iris$Species==input$selectedSpecies),]
selected
})
output$table <- renderDataTable({
DT::datatable(selectedIris())
})
callModule(showDTdata, id="testDTModule", DTdata=selectedIris())
}
# Run the app ----
shinyApp(ui = ui, server = server)
You have to pass the reactive conductor in showDTdata:
showDTdata <- function(input, output, session, DTdata) {
output$table <- renderDataTable({
DT::datatable(DTdata()) # not datatable(DTdata)
})
}
callModule(showDTdata, id="testDTModule", DTdata=selectedIris) # not DTdata=selectedIris()
Does this do what you want? I removed your functions and added the selection ='multiple' to table 1 (tableX) so that we can then listen to tableX_rows_selected
P.S.: I have noticed that if you first load DT and then shiny, that the whole app won't work anymore. This is a bit weird since we call all datatable functions with DT::... but, you do get a warning message that some parts of DT are masked by shiny or viceversa.
require("shiny")
require('DT')
# User interface
ui <-
fluidPage(
sidebarLayout(
sidebarPanel(id="DT",
width = 4,
helpText(h4("Select")),
selectInput("selectedSpecies", label = "Species",
choices = c("setosa","versicolor","virginica"),
selected = "versicolor")
),
mainPanel(
h3("Table 1. Presenting selected data from Iris" ),
DT::dataTableOutput("tablex"),
br(),
h3("Table 2. Presenting selected data from Iris using shiny module"),
DT::dataTableOutput("table2")
)
)
)
# Define server logic ----
server <- function(session, input, output) {
values <- reactiveValues(rowselect = numeric())
selectedIris <- reactive ( {
selected <- iris[which(iris$Species==input$selectedSpecies),]
selected
})
output$tablex <- renderDataTable({
DT::datatable(selectedIris(), selection = 'multiple')
})
IrisSelected <- reactive({
df <- iris[c(input$tablex_rows_selected), ]
df
})
output$table2 <- renderDataTable({
req(nrow(IrisSelected()) > 0)
DT::datatable( IrisSelected())
})
}
# Run the app ----
shinyApp(ui = ui, server = server)
Without knowing of the shiny module approach, I would have probably written it like a normal function. The app below works but I am curious now after seeing the answer by #Stephane what the advantages are of using callModule approach over regular function approach
require("DT")
require("shiny")
makeTable <- function(dataframe) { DT::datatable(dataframe) %>%
formatStyle(names(dataframe), background = '#fff')
}
# User interface
ui <-
fluidPage(
sidebarLayout(
sidebarPanel(id="DT",
width = 4,
helpText(h4("Select")),
selectInput("selectedSpecies", label = "Species",
choices = c("setosa","versicolor","virginica"),
selected = "versicolor")
),
mainPanel(
dataTableOutput('Table1')
)
)
)
# Define server logic ----
server <- function(session, input, output) {
selectedIris <- reactive ( {
selected <- iris[which(iris$Species==input$selectedSpecies),]
selected
})
output$Table1 <- renderDataTable(makeTable(selectedIris()))
}
# Run the app ----
shinyApp(ui = ui, server = server)

Resources