My shiny app reads in an external csv file, updates it using user inputs in the app and updates the external csv file with these. This works locally however with my app on the shiny server it does not update, any idea why this is? Thanks.
I've added some simplistic code below but not really sure if it makes sense, the function num is meant to take the inputs, search for them in data frame and update and then output updated data frame and another data frame called "numbers" with timestamp.
The stuff with the modals at the bottom is that the num() function only happens when button is pressed, this creates modals with shows user what they have inputted and if they press 'proceed' the csv is updated if not they can go back and change details.
ui <- fluidPage(
useShinyjs(),
selectInput("1", "Pick a number", "1-2","2-3"),
selectInput("2", "Pick another number", "4-5", "5-6"),
actionButton("go", "Go")
)
server <- function(input, output, session){
df <- read.csv(df.csv, header = TRUE, sep = ",")
num <- function(df, num1, num2){
date.stamp <- format(Sys.time(), "%Y-%m-%d-%H-%M-%S")
save.file <- paste0("numbers", date.stamp, ".csv")
write.csv(df, file = save.file, row.names = FALSE)
write.csv(df, file = "df.csv", row.names = FALSE)
return(df)
}
observe({
shinyjs::toggleState("go", condition = isTruthy(input$1)
&& isTruthy(input$2))
})
react <- reactiveValues()
observeEvent(input$go, {
react$colm1 <- input$1
react$colm2 <- input$2
react$df1 <- data.frame("Numbers" = c("Num1", "Num2"),
"Values" = c(react$colm1, react$colm2))
react$review <- datatable(rownames = FALSE,
selection = "none",
options = list(dom = "t",
pageLength = nrow(rv$df.check),
autoWidth = FALSE,
columnDefs = list(list(width =
"20%",
targets = c(1)),
list(className = "dt-center",
targets = c(1)))),
react$df.check)
showModal(
modalDialog(
footer = tagList(modalButton("Change numbers"),
actionButton("click", label = "Proceed")
),
strong(em("Inputs Entered")),
p("Please double check numbers"),
DT::renderDataTable({
react$review
})
))
observeEvent(input$click, {
num(df, input$1, input$2)
shinyjs::reset("1");shinyjs::reset("s2")
removeModal()
})
})}
shinyApp(ui = ui, server = server) #calls shiny app
Related
I have a shiny app where the user uploads a csv file. Then, using the column names from the csv file, I create sortable bucket list. I would like drag the column name from the first rank list and have it cloned (i.e. not depleted). I tried to use the options parameter in add_rank_list() setting pull='clone', but that did not work. Any idea on how to do this? Below is my code, and some fake data can be accessed here.
library(shiny)
library(shinyjs)
library(sortable)
ui <- fluidPage(
titlePanel("App"),
sidebarLayout(
sidebarPanel(
useShinyjs(),
fileInput(inputId = "file1", label = "Select a .csv file",
accept = c("text/csv", "text/comma-separated-values,text/plain",".csv")
),
uiOutput("show_button")
),
mainPanel(
DT::dataTableOutput("table")
)
),
fluidRow(uiOutput("buckets"))
)
server <- function(input, output) {
# input csv file
input_file <- reactive({
if (is.null(input$file1)) {
return("")
}
# actually read the file
read.csv(file = input$file1$datapath)
})
# button to hide/show table
## only show when table is loaded
output$show_button = renderUI({
req(input$file1)
actionButton(inputId = "button", label = "show / hide table")
})
## observe the button being pressed
observeEvent(input$button, {
shinyjs::toggle("table")
})
# output table
output$table <- DT::renderDataTable({
# render only if there is data available
req(input_file())
# reactives are only callable inside an reactive context like render
data <- input_file()
data
})
# Drag and Drop Col names
output$buckets = renderUI(
{
# create list of colnames
req(input$file1)
data = input_file()
cols = colnames(data)
# create bucket list
bucket_list(
header = "Drag the items in any desired bucket",
group_name = "bucket_list_group",
orientation = "horizontal",
add_rank_list(
text = "Drag from here",
labels = as.list(cols),
input_id = "rank_list_1",
css_id = "list1",
options = sortable_options(
group = list(
pull = "clone",
name = "list_group1",
put = FALSE))
),
add_rank_list(
text = "to here",
labels = NULL,
input_id = "rank_list_2",
css_id = "list2",
options = sortable_options(group = list(name = "list_group1")))
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I have developed a quite complex Shiny app that helps the user filter an uploaded table of genetics variants. So that, the user can upload a table, apply different filters and see the variants remaining. Now I would like the user to be able to upload the table in two different and excluding ways:
First: the table is straightaway uploaded by the user with fileInput.
Second: the user presses a button that makes the table being applied a series of changes with a python program that works outside shiny, then a processed table is created for the session and uploaded for filtering with another button.
Both options result in an uploaded table that can be filtered with my program, so I would like to conserve same the variable name in both cases. Both processes work perfectly when the other is commented, however I would like to have both uploading options available for the user. Due to the complexity of the program I cannot show a totally reproducible example here, but I can show you the part of the code I want to work in.
library(shiny)
library(DT)
library(shinyWidgets)
library(shinyBS)
library(shinyFiles)
ui = fluidPage(
# Uploading variant table straight away with a file input (way 1):
fileInput("file1", "Upload your SNV File",
multiple = FALSE,
accept = c("text/csv", "text/comma-separated-values,text/plain", ".tsv")),
# User presses a button if changes previous to the upload want to be applied (way 2):
actionBttn(
inputId = "WGS",
label = "Analysis of WGS",
),
# User needs to fill a survey before the python program is launched:
bsModal("survey", "Select WGS data information","WGS",
prettyCheckbox(inputId="canonical_filters", label = "Canonical", value = TRUE),
shinyFilesButton("Btn_GetFile", "Process WGS variant file", title = "WGS variant file:", multiple = FALSE),
actionButton("EnterWGS", "Read file")),
# Table is rendered
DTOutput("contents")
)
server <- function(input, output, session) {
# Datatable is uploaded straight away (WAY 1)
df <- reactive({
req(input$file1)
df <- read.table(input$file1$datapath, fill = TRUE, quote = "", header = TRUE,
sep = '\t', na.strings=c("",".","NA"), colClasses = NA)
})
# The path of the file where the changes are going to be applied can be selected and the python program (process_file.py) is launched with the system function. A processed variant table is created for the session.
observeEvent(input$Btn_GetFile, {
volumes = getVolumes()
shinyFileChoose(input, "Btn_GetFile", roots=volumes, session = session, filetypes = c('', 'txt', "tsv", "csv"))
file_selected<-parseFilePaths(volumes, input$Btn_GetFile)
if (length(file_selected$datapath)!=0){
system('process_file.py', file_selected$datapath )
}
})
# The processed variant table is uploaded when the button is pressed (WAY 2)
df <- eventReactive(input$EnterWGS, {
df <- read.table('temp_file', fill = TRUE, quote = "", header = TRUE,
sep = '\t', na.strings=c("",".","NA"), check.names = FALSE, colClasses = NA)
}, ignoreNULL = T)
# Rest of the functions...
# Table renderization.
output$contents <- renderDT({
req(df())
datatable(
df(),
filter = "top",
class = "display nowrap compact",
escape = FALSE)},
server = FALSE)
}
shinyApp(ui, server)
I really hope this is understandable. Any help would be appreciated.
Thanks a lot,
Rachael
You can define a reactiveValues object to display which is set to table 1 or table 2. Try this
library(shiny)
library(DT)
library(shinyWidgets)
library(shinyBS)
library(shinyFiles)
ui = fluidPage(
# Uploading variant table straight away with a file input (way 1):
fileInput("file1", "Upload your SNV File",
multiple = FALSE,
accept = c("text/csv", "text/comma-separated-values,text/plain", ".tsv")),
# User presses a button if changes previous to the upload want to be applied (way 2):
actionBttn(
inputId = "WGS",
label = "Analysis of WGS",
),
# User needs to fill a survey before the python program is launched:
bsModal("survey", "Select WGS data information","WGS",
prettyCheckbox(inputId="canonical_filters", label = "Canonical", value = TRUE),
shinyFilesButton("Btn_GetFile", "Process WGS variant file", title = "WGS variant file:", multiple = FALSE),
actionButton("EnterWGS", "Read file")),
# Table is rendered
DTOutput("contents")
)
server <- function(input, output, session) {
rv <- reactiveValues(df=NULL)
# Datatable is uploaded straight away (WAY 1)
df1 <- reactive({
req(input$file1)
df <- read.table(input$file1$datapath, fill = TRUE, quote = "", header = TRUE,
sep = '\t', na.strings=c("",".","NA"), colClasses = NA)
})
# The path of the file where the changes are going to be applied can be selected and the python program (process_file.py) is launched with the system function. A processed variant table is created for the session.
observeEvent(input$Btn_GetFile, {
volumes = getVolumes()
shinyFileChoose(input, "Btn_GetFile", roots=volumes, session = session, filetypes = c('', 'txt', "tsv", "csv"))
file_selected<-parseFilePaths(volumes, input$Btn_GetFile)
if (length(file_selected$datapath)!=0){
system('process_file.py', file_selected$datapath )
}
})
# The processed variant table is uploaded when the button is pressed (WAY 2)
df2 <- eventReactive(input$EnterWGS, {
df <- read.table('temp_file', fill = TRUE, quote = "", header = TRUE,
sep = '\t', na.strings=c("",".","NA"), check.names = FALSE, colClasses = NA)
}, ignoreNULL = T)
### condition this observer to display df1()
observeEvent(df1(), {
rv$df <- df1()
})
### condition this observer to display df2()
observeEvent(input$WGS, {
rv$df <- df2()
})
# Rest of the functions...
# Table renderization.
output$contents <- renderDT({
datatable(
rv$df,
filter = "top",
class = "display nowrap compact",
escape = FALSE)},
server = FALSE)
}
shinyApp(ui, server)
I'm pretty new to shiny (being playing around for about a week). And I'm trying to create an app that takes and input tab-separated text file and perform several exploratory functions. In this case I'm presenting a very simplified version of that app just to highlight what I want to do in a specific case:
Problem:
If you try the app with the sample data (or any data in the same format) you can notice that the app effectively performs the default summary table (if selectInput="summarize", then output$sumfile), but when you try to select "explore", the previous table gets removed from the mainPanel, and outputs the full file (selectInput="explore",then output$gridfile) in the place where it would be as if selectInput="summarize" was still selected.
If you re-select "summarize", excelOutput("sumfile") gets duplicated on the mainPanel.
My goal is simple:
excelOutput("sumfile") when selectInput="summarize" ONLY and
excelOutput("gridfile") when selectInput="explore" ONLY
without placement issues or duplications on the mainPanel
So far I've tried:
inFile=input$df
if(is.null(inFile))
return(NULL)
if(input$show=="summarize")
return(NULL)
or
inFile=input$df
if(is.null(inFile))
return(NULL)
if(input$show=="explore")
return(NULL)
To control what shows up on the mainPanel, but with placement and duplication issues.
sample data:
#Build test data
testdat<-data.frame(W=c(rep("A",3),
rep("B",3),
rep("C",3)),
X=c(letters[1:9]),
Y=c(11:19),
Z=c(letters[1:7],"",NA),
stringsAsFactors = FALSE)
#Export test data
write.table(testdat,
"your/path/file.txt",
row.names = FALSE,
sep = "\t",
quote = FALSE,
na="")
shiny app (app.R):
library(shiny)
library(excelR)
#function to summarize tables
Pivot<-function(df){
cclass<-as.character(sapply(df,
class))
df.1<-apply(df,
2,
function(x) unlist(list(nrows = as.numeric(NROW(x)),
nrows.unique = length(unique(x))-(sum(is.na(x))+length(which(x==""))),
nrows.empty = (sum(is.na(x))+length(which(x==""))))))
df.2<-data.frame(df.1,
stringsAsFactors = FALSE)
df.3<-data.frame(t(df.2),
stringsAsFactors = FALSE)
df.3$col.class<-cclass
df.3$col.name<-row.names(df.3)
row.names(df.3)<-NULL
df.3<-df.3[c(5,4,1,2,3)]
return(df.3)
}
ui <- fluidPage(
ui <- fluidPage(titlePanel(title=h1("Summary generator",
align="center")),
sidebarLayout(
sidebarPanel(
h3("Loading panel",
align="center"),
fileInput("df",
"Choose file (format: file.txt)",
accept = c("plain/text",
".txt")),
selectInput("show",
"Choose what to do with file",
choices=c("summarize","explore")),
p("**'summarize' will output a summary of the selected table"),
p("**'explore' will output the full selected editable table"),
tags$hr()
),
mainPanel(
excelOutput("gridfile"),
excelOutput("sumfile")
))))
server <- function(input, output) {
dat<-reactive({
fp<-input$df$datapath
read.delim(fp,
quote="",
na.strings="\"\"",
stringsAsFactors=FALSE)
})
#get summary
output$sumfile<-renderExcel({
inFile=input$df
if(is.null(inFile)) #if fileInput is empty return nothing
return(NULL)
if(input$show=="explore") #if selectInput = "explore" return nothing
return(NULL)
dat.1<-data.frame(dat())
dat.2<-Pivot(dat.1)
excelTable(dat.2,
defaultColWidth = 100,
search = TRUE)
})
#get full file
output$gridfile<-renderExcel({
inFile=input$df
if(is.null(inFile)) #if fileInput is empty return nothing
return(NULL)
if(input$show=="summarize") #if selectInput = "summarize" return nothing
return(NULL)
dat.1<-data.frame(dat())
excelTable(dat.1,
defaultColWidth = 100,
search = TRUE)
})
}
shinyApp(ui = ui, server = server)
One way to do what you want is to use observeEvent for your inputs input$show and input$df and renderExcel based on your selection of `input$show. Here is an updated version for your code:
library(shiny)
library(excelR)
#function to summarize tables
Pivot<-function(df){
cclass<-as.character(sapply(df,
class))
df.1<-apply(df,
2,
function(x) unlist(list(nrows = as.numeric(NROW(x)),
nrows.unique = length(unique(x))-(sum(is.na(x))+length(which(x==""))),
nrows.empty = (sum(is.na(x))+length(which(x==""))))))
df.2<-data.frame(df.1,
stringsAsFactors = FALSE)
df.3<-data.frame(t(df.2),
stringsAsFactors = FALSE)
df.3$col.class<-cclass
df.3$col.name<-row.names(df.3)
row.names(df.3)<-NULL
df.3<-df.3[c(5,4,1,2,3)]
return(df.3)
}
ui <- fluidPage(
ui <- fluidPage(titlePanel(title=h1("Summary generator",
align="center")),
sidebarLayout(
sidebarPanel(
h3("Loading panel",
align="center"),
fileInput("df",
"Choose file (format: file.txt)",
accept = c("plain/text",
".txt")),
selectInput("show",
"Choose what to do with file",
choices=c("summarize","explore")),
p("**'summarize' will output a summary of the selected table"),
p("**'explore' will output the full selected editable table"),
tags$hr()
),
mainPanel(
excelOutput("gridfile"),
excelOutput("sumfile")
))))
server <- function(input, output) {
dat<-reactive({
fp<-input$df$datapath
read.delim(fp,
quote="",
na.strings="\"\"",
stringsAsFactors=FALSE)
})
observeEvent({
input$show
input$df
}, {
inFile=input$df
if(is.null(inFile)) #if fileInput is empty return nothing
return(NULL)
if(input$show=="explore") {
output$gridfile<-renderExcel({
dat.1<-data.frame(dat())
excelTable(dat.1,
defaultColWidth = 100,
search = TRUE)
})
}
if(input$show=="summarize") {
output$sumfile<-renderExcel({
dat.1<-data.frame(dat())
dat.2<-Pivot(dat.1)
excelTable(dat.2,
defaultColWidth = 100,
search = TRUE)
})
}
})
}
shinyApp(ui = ui, server = server)
Hope it helps!
I am looking for suggestions in improving performance of my shiny app. This shiny app saves data in CSV format when user selects a particular row by clicking on check box. I don't want it to save data every time when user clicks on check box. Hence I created action button so that user clicks on button only when he is done with the checkbox selection of multiple rows.
library(shiny)
library(DT)
mydata = mtcars
mydata$id = 1:nrow(mydata)
runApp(
list(ui = pageWithSidebar(
headerPanel('Examples of Table'),
sidebarPanel(
textInput("collection_txt",label="RowIndex")
,br(),
actionButton("run", "Write Data"),
br(),
p("Writeback with every user input. CSV file gets saved on your working directory!")),
mainPanel(
DT::dataTableOutput("mytable")
))
, server = function(input, output, session) {
shinyInput <- function(FUN,id,num,...) {
inputs <- character(num)
for (i in seq_len(num)) {
inputs[i] <- as.character(FUN(paste0(id,i),label=NULL,...))
}
inputs
}
rowSelect <- reactive({
rows=names(input)[grepl(pattern = "srows_",names(input))]
paste(unlist(lapply(rows,function(i){
if(input[[i]]==T){
return(substr(i,gregexpr(pattern = "_",i)[[1]]+1,nchar(i)))
}
})))
})
observe({
updateTextInput(session, "collection_txt", value = rowSelect() ,label = "RowIndex:" )
d = data.frame(n = rowSelect(), stringsAsFactors = F)
if (input$run == 0)
return()
isolate({write.csv(mydata[as.numeric(d$n),], file = "Writeback.csv" , row.names=F)})
})
output$mytable = DT::renderDataTable({
DT::datatable(cbind(Flag=shinyInput(checkboxInput,"srows_",nrow(mydata),value=NULL,width=1),
mydata), extensions = 'Buttons', options = list(orderClasses = TRUE,
pageLength = 5, lengthChange = FALSE, dom = 'Bfrtip',
buttons = c('copy', 'csv', 'excel'),
drawCallback= JS(
'function(settings) {
Shiny.bindAll(this.api().table().node());}')
),escape=F)
}
)
}), launch.browser = T
)
I want action button to write data in CSV format only when user clicks on action button. Is there any way to improve the code below.
d = data.frame(n = rowSelect(), stringsAsFactors = F)
if (input$run == 0)
return()
isolate({write.csv(mydata[as.numeric(d$n),], file = "Writeback.csv" , row.names=F)})
Why are you not using observeEvent for the actionButton?
observeEvent(input$run, {
updateTextInput(session, "collection_txt", value = rowSelect() ,label = "RowIndex:" )
d = data.frame(n = rowSelect(), stringsAsFactors = F)
write.csv(mydata[as.numeric(d$n),], file = "Writeback.csv" , row.names=F)
})
I think you're looking for the require function req(input$run) or try if(is.null(input$run)==T){return()}
I recently updated R and shiny.
The problem I am having is translating Shiny code into shiny modules. I thought the RStudio guide was very good, but I seem to be missing something fundamental. I would love any direct guidance on the project I am working on. I would also love recommended principles for how to convert from Shiny code to modularized code, particularly hints missing from the RStudio article
My goal in the shiny app below (which is part of a larger project) is to create an app that allows someone to load a .csv file, transform that file into a dissimilarity matrix, and download the matrix.
I have code that works in Shiny, but I haven't been able to convert it into a modularized version.
Note: Because I couldn't figure out how to add a separate .csv file for people to use when evaluating this code, and I wanted to make sure I included sample code, I tossed in sample data and commented out the code that would have allowed people to select an external .csv file. I hope that doesn't mess things up too much.
library(dplyr)
library(cluster)
library(shiny)
bin <- sample(letters[1:10], 50, replace = TRUE) %>% as.factor()
df <- matrix(bin, ncol = 5) %>% as.data.frame()
# User Input Function
cardsortInput <- function(id) {
ns <- NS(id)
tagList(
fileInput(
inputId = "datafile",
label = "Card Sort Results (.csv)",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
checkboxInput(inputId = "header",
label = "Header",
value = TRUE),
checkboxInput(inputId = "strings",
label = "Strings as Factors",
value = TRUE),
selectInput(inputId = "dmetric",
label = "Distance Metric",
choices = c("Euclidean" = "euclidean",
"Manhattan" = "manhattan",
"Gower" = "gower"),
selected = "gower"),
checkboxInput(inputId = "standard",
label = "Data Standardization",
value = FALSE),
downloadButton('downloadData', 'Download Dissimilarity Matrix'),
br(),
br(),
tabsetPanel(
tabPanel(
"Card Sort Data",
tableOutput(
outputId = "cs_table")
),
tabPanel(
"Dissimilarity Matrix",
tableOutput(
outputId = "cs_dissMatrix")
)
)
)
}
# Server Function
cardsort <- function(input, output, session) {
userFile <- reactive({
validate(need(input$datafile, message = FALSE))
input$datafile
})
#dataframe <- reactive({
# read.csv(
# file = userFile()$data.path,
# header = input$header,
#stringsAsFactors = TRUE)
#})
dataframe <- reactive({df
})
diss_matrix <- reactive({
data <- dataframe()
if (is.null(data))
return(NULL)
daisy(
x = data,
metric = input$dmetric,
stand = input$standard) %>%
as.matrix()
})
output$dissMatrix <- renderTable({
diss_matrix()
})
output$downloadData <- downloadHandler(
filename = function() {paste("data-",
Sys.Date(),
".csv", sep="")
},
content = function(file) {
write.csv(diss_matrix(),
file,
row.names = FALSE)
}
)
}
#########
ui <- fluidPage(
cardsortInput("test")
)
server <- function(input, output, session) {
datafile <- callModule(cardsort, "test")
}
shinyApp(ui = ui, server = server)