Non-reactive legend in Shiny - r

How can I create a static legend in this Shiny App?
The legend must contain all 4 anomaly factor levels, regardless if they are present in the reactive plot. The factor levels are NORMAL, TENTATIVE, LOW, and HIGH
The input data-frame is automatically created in the script below.
The color and shape of the legend points and plot points should match.
I also must keep the hover information presently coded into the aes_string()
# Load libraries
library(dplyr)
library(shiny)
library(plotly)
library(ggplot2)
library(dplyr)
library(scales)
library(shinyWidgets)
library(lubridate)
# Create input dataframe
DF <- data.frame(
recordID = as.factor(c(101, 102, 103, 104, 105, 106, 107, 108)),
Category = as.factor(c('X', 'X', 'Z', 'Z', 'Z', 'Z', 'X', 'X')),
CategoryTRUEFALSE = c(TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE),
startDate = as_date(c('2022-01-01', '2022-01-02', '2022-01-03','2022-01-04', '2015-08-18', '2015-08-19', '2015-08-20','2015-08-21')),
companyName = as.factor(c('CompanyZ', 'CompanyZ', 'CompanyZ', 'CompanyZ', 'CompanyA', 'CompanyA', 'CompanyA', 'CompanyA')),
wayPoint = as.factor(c('WP1', 'WP1', 'WP1', 'WP1', 'WP2', 'WP2', 'WP2', 'WP2')),
Capacity = c(8000, 8000, 8000, 8000 , 13000, 13000, 13000, 13000),
finalDestination = as.factor(c('PortA', 'PortA', 'PortA', 'PortA', 'PortB', 'PortB', 'PortB', 'PortB')),
Duration = (c(15, 17, 16, 40, 109, 111, 125, 177)),
Anomaly = (c('NORMAL', 'LOW', 'NORMAL', 'HIGH', 'NORMAL', 'TENTATIVE', 'NORMAL', 'HIGH'))
) %>%
mutate(Anomaly = factor(Anomaly, levels = c('NORMAL', 'TENTATIVE', 'LOW', 'HIGH')))
# Info columns
VARS_info <- c('recordID', 'startDate', 'Category', 'CategoryTRUEFALSE', 'Duration', 'Anomaly')
# Declare selector variables
VARS_selector <- c('companyName', 'wayPoint', 'Capacity', 'finalDestination')
# UI
ui <- navbarPage(title = "Anomaly Browser",
tabPanel("Browse data",
sidebarLayout(
sidebarPanel(
selectInput(inputId = "companyName",
label = "Rail haul provider: ",
choices = sort(unique(Shiny$companyName)),
multiple = FALSE),
selectInput(inputId = "wayPoint",
label = "Load point: ",
choices = NULL,
multiple = FALSE),
selectInput(inputId = "capacity",
label = "Capacity: ",
choices = NULL,
multiple = FALSE),
selectInput(inputId = "finalDestination",
label = "Terminal: ",
choices = NULL,
multiple = FALSE),
br(),
br(),
switchInput(inputId = "category",
onLabel = "X",
offLabel = "Z",
onStatus = "GreenStatus",
offStatus = "RedStatus",
inline = TRUE,
value = TRUE,
size = 'large'
),
br(),
br(),
downloadLink("downloadData", "Download plot data"),
br(),
width = 2,
# switchInput color while on
tags$head(tags$style(HTML('.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-GreenStatus,
.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-GreenStatus {
background: green;
color: white;
}'))),
# switchInput color while off
tags$head(tags$style(HTML('.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-RedStatus,
.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-RedStatus {
background: darkred;
color: white;
}'))),
),
mainPanel(
plotlyOutput(outputId = "scatterplot", width = "120%", height = "800px"),
DT::dataTableOutput(outputId = "Table1", width = "125%")
))))
# Server
server <- function(input, output, session) {
observeEvent(input$companyName,{
updateSelectInput(session,'wayPoint',
choices=sort(unique(Shiny$wayPoint[Shiny$companyName %in% input$companyName])))
})
observeEvent(input$wayPoint,{
updateSelectInput(session,'capacity',
choices=sort(unique(Shiny$Capacity[Shiny$wayPoint %in% input$wayPoint &
Shiny$companyName %in% input$companyName])))
})
observeEvent(input$capacity,{
updateSelectInput(session,'finalDestination',
choices=sort(unique(Shiny$finalDestination[Shiny$Capacity == input$capacity &
Shiny$wayPoint %in% input$wayPoint &
Shiny$companyName %in% input$companyName])))
})
observeEvent(input$wayPoint,{
updateSelectInput(session,'finalDestination',
choices=sort(unique(Shiny$finalDestination[Shiny$Capacity == input$capacity &
Shiny$wayPoint %in% input$wayPoint &
Shiny$companyName %in% input$companyName])))
})
observeEvent(input$finalDestination,{
updateSelectInput(session,'category',
choices=sort(unique(Shiny$Category[Shiny$finalDestination %in% input$finalDestination &
Shiny$Capacity == input$capacity &
Shiny$wayPoint %in% input$wayPoint &
Shiny$companyName %in% input$companyName])))
})
# Selected
selected1 <- reactive({
req(input$companyName, input$wayPoint, input$capacity, input$finalDestination)
Shiny %>%
select(all_of(VARS_info), all_of(VARS_selector)) %>%
filter(companyName %in% input$companyName &
wayPoint %in% input$wayPoint &
Capacity == input$capacity &
finalDestination %in% input$finalDestination &
CategoryTRUEFALSE %in% input$category) %>%
select(-CategoryTRUEFALSE)
})
# Create scatterplot object the plotOutput function is expecting
output$scatterplot <- renderPlotly({
p <- ggplot(data = selected1(), aes_string("startDate", "Duration",
A = "startDate", B = "Duration", C = "recordID", D = 'Anomaly'))
p <- p + ggtitle(paste0(input$companyName, " - ", input$wayPoint, " - ", input$finalDestination, " - ", input$capacity, " (", unique(selected1()$Category), ")")) +
xlab('Cycle Start Date') + ylab("Duration (mins)") + theme(text = element_text(size = 13))
p <- p + scale_x_date(date_breaks = "months", date_labels = "%b-%Y") +
geom_smooth(method = "gam", formula = y ~ s(x, bs = "cs", k = 1), colour = "black", lwd = 0.7, se = FALSE)
p <- p + geom_point(data = selected1()[which(selected1()$Anomaly=='NORMAL'),],
pch=21, fill= NA, size=1.0, colour="darkgreen", stroke=1.5)
p <- p + geom_point(data = selected1()[which(selected1()$Anomaly=='TENTATIVE'),],
pch=21, fill= NA, size=1.0, colour="royalblue3", stroke=1.5)
p <- p + geom_point(data = selected1()[which(selected1()$Anomaly=='LOW'),],
pch=21, fill= NA, size=1.0, colour="orange", stroke=1.5)
p <- p + geom_point(data = selected1()[which(selected1()$Anomaly=='HIGH'),],
pch=21, fill= NA, size=1.0, colour="red", stroke=1.5)
ggplotly(p, tooltip = c("A", "B", "C", "D"))
})
# Data table Tab-1
output$Table1 <- DT::renderDataTable({
DT::datatable(data = selected1(),
options = list(pageLength = 20),
rownames = FALSE)
})
# Save CSV
output$downloadData <- downloadHandler(
filename = function() {paste0(input$companyName,'_',input$wayPoint,'_',input$finalDestination,'_',unique(selected1()$Category),'_','cap=',input$capacity,'.csv')},
content = function(file) {
write.csv(selected1(), file, row.names = FALSE)
})
}
# Create a Shiny app object
shinyApp(ui = ui, server = server)

We can force ggplot to display all legend items by providing a dummy data.frame containing all levels available in the dataset.
Furthermore, I'm using scale_colour_manual to reduce the code:
# Load libraries
library(dplyr)
library(shiny)
library(plotly)
library(ggplot2)
library(dplyr)
library(scales)
library(shinyWidgets)
library(lubridate)
# Create input dataframe
DF <- data.frame(
recordID = as.factor(c(101, 102, 103, 104, 105, 106, 107, 108)),
Category = as.factor(c('X', 'X', 'Z', 'Z', 'Z', 'Z', 'X', 'X')),
CategoryTRUEFALSE = c(TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE),
startDate = as_date(c('2022-01-01', '2022-01-02', '2022-01-03','2022-01-04', '2015-08-18', '2015-08-19', '2015-08-20','2015-08-21')),
companyName = as.factor(c('CompanyZ', 'CompanyZ', 'CompanyZ', 'CompanyZ', 'CompanyA', 'CompanyA', 'CompanyA', 'CompanyA')),
wayPoint = as.factor(c('WP1', 'WP1', 'WP1', 'WP1', 'WP2', 'WP2', 'WP2', 'WP2')),
Capacity = c(8000, 8000, 8000, 8000 , 13000, 13000, 13000, 13000),
finalDestination = as.factor(c('PortA', 'PortA', 'PortA', 'PortA', 'PortB', 'PortB', 'PortB', 'PortB')),
Duration = (c(15, 17, 16, 40, 109, 111, 125, 177)),
Anomaly = (c('NORMAL', 'LOW', 'NORMAL', 'HIGH', 'NORMAL', 'TENTATIVE', 'NORMAL', 'HIGH'))
) %>% mutate(Anomaly = factor(Anomaly, levels = c('NORMAL', 'TENTATIVE', 'LOW', 'HIGH')))
DF <- with(DF, DF[order(Anomaly),])
dummyDF <- DF[!duplicated(DF$Anomaly),]
dummyDF$startDate <- as.Date(NA)
colours = c("NORMAL" = "darkgreen", "TENTATIVE" = "royalblue3", "LOW" = "orange", "HIGH" = "red")
# Info columns
VARS_info <- c('recordID',
'startDate',
'Category',
'CategoryTRUEFALSE',
'Duration',
'Anomaly')
# Declare selector variables
VARS_selector <- c('companyName', 'wayPoint', 'Capacity', 'finalDestination')
# UI
ui <- navbarPage(title = "Anomaly Browser",
tabPanel("Browse data",
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "companyName",
label = "Rail haul provider: ",
choices = sort(unique(DF$companyName)),
multiple = FALSE
),
selectInput(
inputId = "wayPoint",
label = "Load point: ",
choices = NULL,
multiple = FALSE
),
selectInput(
inputId = "capacity",
label = "Capacity: ",
choices = NULL,
multiple = FALSE
),
selectInput(
inputId = "finalDestination",
label = "Terminal: ",
choices = NULL,
multiple = FALSE
),
br(),
br(),
switchInput(
inputId = "category",
onLabel = "X",
offLabel = "Z",
onStatus = "GreenStatus",
offStatus = "RedStatus",
inline = TRUE,
value = TRUE,
size = 'large'
),
br(),
br(),
downloadLink("downloadData", "Download plot data"),
br(),
width = 2,
# switchInput color while on
tags$head(tags$style(
HTML(
'.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-GreenStatus,
.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-GreenStatus {
background: green;
color: white;
}'
)
)),
# switchInput color while off
tags$head(tags$style(
HTML(
'.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-RedStatus,
.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-RedStatus {
background: darkred;
color: white;
}'
)
)),
),
mainPanel(
plotlyOutput(
outputId = "scatterplot",
width = "120%",
height = "800px"
),
DT::dataTableOutput(outputId = "Table1", width = "125%")
)
)))
# Server
server <- function(input, output, session) {
observeEvent(input$companyName, {
updateSelectInput(session, 'wayPoint',
choices = sort(unique(DF$wayPoint[DF$companyName %in% input$companyName])))
})
observeEvent(input$wayPoint, {
updateSelectInput(session, 'capacity',
choices = sort(unique(DF$Capacity[DF$wayPoint %in% input$wayPoint &
DF$companyName %in% input$companyName])))
})
observeEvent(input$capacity, {
updateSelectInput(session, 'finalDestination',
choices = sort(unique(DF$finalDestination[DF$Capacity == input$capacity &
DF$wayPoint %in% input$wayPoint &
DF$companyName %in% input$companyName])))
})
observeEvent(input$wayPoint, {
updateSelectInput(session, 'finalDestination',
choices = sort(unique(DF$finalDestination[DF$Capacity == input$capacity &
DF$wayPoint %in% input$wayPoint &
DF$companyName %in% input$companyName])))
})
observeEvent(input$finalDestination, {
updateSelectInput(session, 'category',
choices = sort(unique(DF$Category[DF$finalDestination %in% input$finalDestination &
DF$Capacity == input$capacity &
DF$wayPoint %in% input$wayPoint &
DF$companyName %in% input$companyName])))
})
# Selected
selected1 <- reactive({
req(input$companyName,
input$wayPoint,
input$capacity,
input$finalDestination)
DF %>%
select(all_of(VARS_info), all_of(VARS_selector)) %>%
filter(
companyName %in% input$companyName &
wayPoint %in% input$wayPoint &
Capacity == input$capacity &
finalDestination %in% input$finalDestination &
CategoryTRUEFALSE %in% input$category
) %>%
select(-CategoryTRUEFALSE)
})
# Create scatterplot object the plotOutput function is expecting
output$scatterplot <- renderPlotly({
p <- ggplot(
data = dummyDF,
aes(x = startDate, y = Duration, color = Anomaly, A = startDate, B = Duration, C = recordID, D = Anomaly)
) + geom_point(
pch = 21,
fill = NA,
size = 1.0,
stroke = 1.5
) + geom_point(
data = selected1(),
pch = 21,
fill = NA,
size = 1.0,
stroke = 1.5
) + scale_colour_manual(values = colours)
p <- p + ggtitle(
paste0(
input$companyName,
" - ",
input$wayPoint,
" - ",
input$finalDestination,
" - ",
input$capacity,
" (",
unique(selected1()$Category),
")"
)
) +
xlab('Cycle Start Date') + ylab("Duration (mins)") + theme(text = element_text(size = 13))
p <- p + scale_x_date(date_breaks = "months", date_labels = "%b-%Y") +
geom_smooth(
method = "gam",
formula = y ~ s(x, bs = "cs", k = 1),
colour = "black",
lwd = 0.7,
se = FALSE
)
ggplotly(p, tooltip = c("A", "B", "C", "D")) %>% layout(legend = list(
itemclick = FALSE,
itemdoubleclick = FALSE,
groupclick = FALSE,
itemsizing = "constant",
itemwidth = 100
# x = [...],
# xanchor = [...],
# y = [...],
# yanchor = [...]
))
})
# Data table Tab-1
output$Table1 <- DT::renderDataTable({
DT::datatable(
data = selected1(),
options = list(pageLength = 20),
rownames = FALSE
)
})
# Save CSV
output$downloadData <- downloadHandler(
filename = function() {
paste0(
input$companyName,
'_',
input$wayPoint,
'_',
input$finalDestination,
'_',
unique(selected1()$Category),
'_',
'cap=',
input$capacity,
'.csv'
)
},
content = function(file) {
write.csv(selected1(), file, row.names = FALSE)
}
)
}
# Create a Shiny app object
shinyApp(ui = ui, server = server)
I also provided a layout call on ggplotly to avoid legend clicks, to have a fully static legend. Not sure if this is needed, though.
Regarding the legend position please run schema() and navigate:
object ► layout ► layoutAttributes ► legend ► x
for more information on the parameters, e.g.:
Sets the x position (in normalized coordinates) of the legend.
Defaults to 1.02 for vertical legends and defaults to 0 for
horizontal legends.
Here a related post concerning the legend item size can be found.

Related

Some errors happened when I tried to change a value's type in reactive() function

I use ggsurvplot to draw a survival curve, and I want to input text to the parameter P value. When the input content is character, it can be displayed correctly, however, when the input content is numeric, an error will occur.
The input data is as follows:
enter image description here
The full code is as follows:
rm(list = ls())
options(scipen = 200)
options(encoding = "UTF-8")
options(stringsAsFactors = TRUE)
library(survival)
library(survminer)
library(shiny)
library(bslib)
library(shinythemes)
mIHC <<- read.csv("0 expr.csv",header=TRUE,row.names=1,check.names = FALSE)
gene_list <<- colnames(mIHC)[3: dim(mIHC)[2]]
gene_list_order = gene_list[order(gene_list)]
ui <- fixedPage(
tags$style(HTML("
.navbar .navbar-header {float: left}
.navbar .navbar-nav {float: right}
")
),
navbarPage(
windowTitle = "GMAP",
fluid = TRUE,
# theme = bs_theme(bootswatch = "flatly",),
title = span("GMAP"),
tabPanel(
"Introduction",
),
tabPanel(
"Survival analysis",
sidebarLayout(
sidebarPanel(width = 5,
selectInput("gene_name", "Gene symbol", choices = gene_list_order),
sliderInput("cutoff_per", "Cutoff percent",
value = 0.5, min = 0, max = 0.99, step = 0.01,
ticks = TRUE)
),
mainPanel(width = 7,
tabsetPanel(
tabPanel("Plot",
plotOutput("surv", width = "420px", height = "400px"),
downloadButton('downloadPlot','Download Plot')),
tabPanel("Summary"),
tabPanel("Table")
)
)
)
),
tabPanel(
"Statistics analysis",
),
tabPanel(
"Heatmap"
),
tabPanel(
"About"
)
)
)
server <- function(input, output, session) {
env <- parent.frame()
plot2 <- reactive({
gene_name = input$gene_name
cutoff_per = input$cutoff_per
surv_gene = mIHC[ , c("OS", "event", gene_name)]
plot(surv_gene$OS, surv_gene$event)
})
surv_plot <- reactive({
gene_name = input$gene_name
cutoff_per = input$cutoff_per
surv_gene = mIHC[ , c("OS", "event", gene_name)]
surv_temp = surv_gene
surv_temp = cbind(surv_temp,surv_temp[,1])
colnames(surv_temp) = c("OS", "event", gene_name, "group")
for (row_place in 1: dim(surv_temp)[1]) {
if(surv_temp[row_place, 3] > quantile(surv_temp[,3], cutoff_per)) {
surv_temp[row_place, "group"] = "high"
} else {
surv_temp[row_place, "group"] = "low"
}
}
surv_gene <- surv_temp
fit <- eval(parse(text = paste0("survfit(Surv(OS, event) ~ group, data = surv_gene)")))
p_val = surv_pvalue(fit, data = surv_gene, method = "1")
p_val = round(as.numeric(p_val),2)
# p_val = as.character(p_val)
# p_val = "abc"
ggsurv_doc <- eval(parse(text = paste0("survfit(Surv(OS, event) ~ group, data = surv_gene)")))
ggpar(
ggsurvplot(ggsurv_doc,
data = surv_gene,
# ggtheme = theme_bw(),
conf.int = F,
censor = T,
palette = c("#DC143C", "#4071B3"),
legend.title = colnames(surv_gene)[3],
pval = paste("P =", p_val),
# pval = T,
legend.labs=c("High", "Low"),
# legend.labs=unique(surv_gene$group),
surv.median.line = "hv",
break.time.by = 12,
xlab = "Time (months)",
),
font.main = 13,
font.submain = 13,
font.x = 13,
font.y = 13,
font.caption = 13,
font.title = 13,
font.subtitle = 13,
font.legend = 13,
font.tickslab = 13,
)
})
output$surv <- renderPlot({
surv_plot()
}, res = 96)
output$downloadPlot <- downloadHandler(
filename = function() {
paste("plot.pdf")
},
content = function(file) {
pdf(file, width = 4.5,height = 4.5)
print(surv_plot(), newpage = FALSE)
dev.off()
}
)
}
shinyApp(ui, server)
enter image description here
It works correctly when p_val is a character, as follow:
p_val = "abc"
enter image description here
This looks wrong:
p_val = surv_pvalue(fit, data = surv_gene, method = "1")
p_val = round(as.numeric(p_val),2)
surv_pvalue returns a data.frame but you seem to be treating it like a numeric. Perhaps try:
p_val = surv_pvalue(fit, data = surv_gene, method = "1")$pval[[1]]
p_val = round(as.numeric(p_val),2)

How do I dynamically update a calculated value based on the number of input values in R Shiny?

I am trying to update the value in a plotly chart in R shiny whose calculated value depends on the number of inputs
library(shiny)
library(httr)
library(jsonlite)
library(dplyr)
library(plotly)
library(shinythemes)
library(flexdashboard)
library(shinydashboard)
setwd("X:/Work/Covid-19 Project/Shiny Dashboard")
rp_1 <- read.csv("Data/Risk Profile 1.csv")
rp_2 <- read.csv("Data/Risk Profile 2.csv")
gender <- c("Male","Female")
age <- c("Less than 20 years", "20 to 50 years","More than 50 years")
city <- c("Delhi","Chennai")
diabetes <- c("Have diabetes","Don't have diabetes")
hypertension <- c("Have hypertension","Don't have hypertension")
risk_level_est <- function(city, gender, age, db, ht){
p_inv <- as.numeric(rp_1 %>%
filter(City == city & Gender == gender) %>%
select(Prob))
p_adv <- as.numeric(rp_2 %>%
filter(Age == age & Diabetes == db & Hypertension == ht) %>%
summarise(Hosp + Death))
as.numeric(p_inv*p_adv*100)
}
sar_risk_level_est <- function(age, db, ht){
p_adv <- as.numeric(rp_2 %>%
filter(Age == age & Diabetes == db & Hypertension == ht) %>%
summarise(Hosp + Death))
as.numeric(0.2*p_adv*100)
}
about_page <- tabPanel(
title = "About",
titlePanel("About"),
"Created with R Shiny",
br(),
"2021 April"
)
main_page <- tabPanel(
title = "Estimator",
titlePanel(""),
sidebarLayout(
sidebarPanel(
title = "Inputs",
selectInput("gender", "Select your gender", gender),
selectInput("age", "Select your age", age),
selectInput("city", "Select your city", city),
selectInput("db", "Do you have diabetes", diabetes),
selectInput("ht", "Do you have hypertension", hypertension),
radioButtons("radio", "Do you want to include your household members",
choices = list("No" = 1,"Yes" = 2)),
conditionalPanel("input.radio == 2",
numericInput("members", label = "How many household members do you have?", value='1'),
uiOutput("member_input")
),
actionButton("risk","Calculate my risk profile")
),
mainPanel(
tabsetPanel(
tabPanel(
title = "Risk Profile",
plotlyOutput("risk_profile", height = 250, width = "75%"),
plotlyOutput("overall_risk_profile", height = 250, width = "75%")
)
)
)
)
)
ui <- navbarPage(
title = "Risk Estimator",
theme = shinytheme('united'),
main_page,
about_page
)
server <- function(input, output, session) {
output$member_input <- renderUI({
numMembers <- as.integer(input$members)
lapply(1:numMembers, function(i) {
list(tags$p(tags$u(h4(paste0("Member ", i)))),
selectInput(paste0("age", i), "Select their age", age, selected = NULL),
selectInput(paste0("db", i), "Do they have diabetes", diabetes, selected = NULL),
selectInput(paste0("ht", i), "Do they have hypertension", hypertension, selected = NULL))
})
})
risk_level <- eventReactive(input$risk, {
risk_level_est(input$city, input$gender, input$age, input$db, input$ht)
})
sar_risk_level <- eventReactive(input$risk,{
sar_risk <- 0
lapply(1:input$members, function(i){
sar_risk <- sar_risk + sar_risk_level_est(input[[paste0("age", i)]],input[[paste0("db", i)]],input[[paste0("ht", i)]])
})
as.numeric(sar_risk)
})
output$risk_profile <- renderPlotly({
fig <- plot_ly(
domain = list(x = c(0, 1), y = c(0, 1)),
value = risk_level(),
title = list(text = "Personal Risk Profile"),
type = "indicator",
mode = "gauge+number",
gauge = list(
axis = list(range = list(0, 15)),
bar = list(color = "gray"),
bgcolor = "white",
borderwidth = 2,
bordercolor = "gray",
steps = list(
list(range = c(0, 3.75), color = "darkgreen"),
list(range = c(3.75, 7.5), color = "chartreuse"),
list(range = c(7.5,11.25), color = "orange"),
list(range = c(11.25,15), color = "red")
)))
fig <- fig %>% layout(margin = list(l=30, r=30, t=80, b=30))
fig
})
output$overall_risk_profile <- renderPlotly({
fig <- plot_ly(
domain = list(x = c(0, 1), y = c(0, 1)),
value = risk_level() + sar_risk_level(),
title = list(text = "Overall Risk Profile"),
type = "indicator",
mode = "gauge+number",
gauge = list(
axis = list(range = list(0, 15+(25*input*members))),
bar = list(color = "gray"),
bgcolor = "white",
borderwidth = 2,
bordercolor = "gray",
steps = list(
list(range = c(0, 3.75), color = "darkgreen"),
list(range = c(3.75, 7.5), color = "chartreuse"),
list(range = c(7.5,11.25), color = "orange"),
list(range = c(11.25,15), color = "red")
)))
fig <- fig %>% layout(margin = list(l=30, r=30, t=80, b=30))
fig
})
}
shinyApp(ui, server)
While the risk_profile plot works fine, the overall_risk_profile plot throws the "non-numeric argument to binary operator" error. The sar_risk_level() value in overall_risk_profile is dependent on a calculation (sar_risk_level_est) which depends on the number of inputs. I want this value (sar_risk) to be initizialied to zero and updated everytime the action button is pressed.
Great looking app. I think it is just a typo. The code has 25*input*members instead of 25*input$members on line 151.

Saving Dynamically Generated Plots in Shiny

so I have recently adapted some code that I found on StackOverflow to create a dynamic number of plots based on user input. However, I now cannot figure out how to save all of those dynamic plots in one file; when I use ggsave() in downloadHandler, it only saves the last plot generated, as the plots are created inside of a for loop, inside of an observe function. I have tried saving the for loop as a separate function and saving that instead of last plot, I have tried saving the observe() as a function and calling that inside ggsave(), but nothing works. Any idea how I can save all of the generated plots to one file?
ui <- fluidPanel(
sidebarLayout(
sidebarPanel(
#this is the input widget for dataset selection
selectInput(inputId = "dataset_selec",
label = "Choose which Dataset to explore:",
choices = list("NK AD Dataset (Zhang, 2020)",
"APPPS1 Dataset (Van Hove, 2019)",
"Aging T Cell Dataset (Dulken, 2019)"),
selected = "APPPS1 Dataset (Van Hove, 2019)"))
mainPanel(
fluidRow(
column(4,
textInput(inputId = "gene_fp",
label = "Enter gene(s) of interest here, separated by commas: ")
),
column(4,
br(),
checkboxInput("split_fp", "Split the graph?")
),
column(4,
conditionalPanel(condition = "input.split_fp == true",
#display choices to split by
selectInput(inputId = "metadata_split_fp",
label = "Choose how to split the Seurat data: ",
choices = list("Genotype", "Timepoint")))
)
),
#ask users if they want to split the graphs
br(),
fluidRow(
column(4,
textInput("save_name_fp",
label = "Enter a file name: ")
),
column(4,
conditionalPanel(condition = "input.save_name_fp.length > 0",
selectInput("fp_device",
label = "Select file type: ",
choices = list("PNG", "JPEG", "PDF", "TIFF",
"BMP", "SVG")))
),
column(4,
br(),
conditionalPanel(condition = "input.save_name_fp.length > 0",
downloadButton("fp_save", label = "Save Feature Plot"))
)
),
#plot the actual plot
uiOutput("fp_plots")
)
)
)
server <- function(input, output, session) {
datasetInput <- reactive({
switch(input$dataset_selec,
"NK AD Dataset (Zhang, 2020)" = nk_data,
"APPPS1 Dataset (Van Hove, 2019)" = appps1_data,
"Aging T Cell Dataset (Dulken, 2019)" = tcellinfil_data)
})
output$fp_plots <- renderUI({
#validate is to prevent an error message from being displayed when a gene hasn't been entered yet
validate(
need(input$gene_fp !="", "Please enter a gene.")
)
fp_genes <- input$gene_fp
fp_genes <- gsub(" ", "", fp_genes)
fp_genes <- unlist(strsplit(fp_genes, split = ","))
n <- length(fp_genes)
plot_output_list <- lapply(1:n, function(i) {
plotname <- paste("plot", i, sep = "")
if (input$split_fp == TRUE) {plotOutput(plotname, height = 580, width = 1100)}
else {plotOutput(plotname, height = 580, width = 550)}
})
do.call(tagList, plot_output_list)
})
#Here, we take the input of genes, and turn it into a character vector, so that we can iterate
#over it. This needs to be under observe({}) because it involves an input.
#Next, we iterate through the list of genes using a for loop, and within that for loop we assign
#the plots that we want to be displayed to each plotname, which is also sequentially created within
#this for loop, and assign it to the tagList we generated earlier. Basically, we're adding objects to
#list of names we made earlier.
#This needs to be under local({}) control, otherwise each graph doesn't get its own number,
#because of when renderPlot is evaluated
observe({
fp_genes <- input$gene_fp
fp_genes <- gsub(" ", "", fp_genes)
fp_genes <- unlist(strsplit(fp_genes, split = ","))
for (i in 1:length(fp_genes)) {
local({
plotname <- paste("plot", i, sep = "")
gene <- fp_genes[i]
output[[plotname]] <- renderPlot({
if (input$split_fp == TRUE) {FeaturePlot(datasetInput(), features = gene, split.by = input$metadata_split_fp)}
else {FeaturePlot(datasetInput(), features = gene)}
})
})
}
})
output$fp_save <- downloadHandler(
filename = function() {
paste(input$save_name_fp, tolower(input$fp_device), sep = ".")
},
content = function(file) {
ggsave(file, device = tolower(input$fp_device))
}
)
}
Create a list of plots, use grid.arrange to save it in a format you wish, and then save it. Perhaps you can adapt this code.
library(shiny)
library(dplyr)
library(tidyr)
library(ggplot2)
library(gridExtra)
mean_data <- data.frame(
Name = c(paste0("Group_", LETTERS[1:20])),
matx <- matrix(sample(1:1000, 1000, replace = T), nrow = 20)
)
names(mean_data)[-1] <- c(paste0("Gene_", 1:50))
sd_data <- data.frame(
Name = c(paste0("Group_", LETTERS[1:20])),
matx <- matrix(runif(1000, 5, 10), nrow = 20)
)
names(sd_data)[-1] <- c(paste0("Gene_", 1:50))
# Prepare dataset.
# 1. Bind mean and sd data
# 2. Reshape
data <- bind_rows(list(
mean = mean_data,
sd = sd_data
), .id = "stat")
data_mean_sd1 <- data %>%
pivot_longer(-c(Name, stat), names_to = "Gene", values_to = "value") %>%
pivot_wider(names_from = "stat", values_from = "value")
###
ui <- fluidPage(
pageWithSidebar(
headerPanel("Gene_FPKM Value Barplot"),
sidebarPanel(
selectInput(
"selectGeneSymbol",
"Select Gene Symbol:",
choices = unique(data_mean_sd1$Gene),
multiple =F,
width = 400,
selected = 1 #"Igfbp7"
),
selectInput(
"selectGeneSymbol2",
"Select Gene Symbol2:",
choices = unique(data_mean_sd1$Gene),
multiple =F,
width = 400,
selected = 1 #"Igfbp7"
),
selectInput("fp_device",
label = "Select file type: ",
choices = list("PNG", "JPEG", "PDF", "TIFF","BMP", "SVG")
),
actionButton(inputId = "plot1", label = "FPKM"),
actionButton(inputId = "plot2", label = "logFC"),
actionButton(inputId = "all",label = "logFC&FPKM"),br(),
downloadButton("fp_save", label = "Save Feature Plot")
),
mainPanel(
uiOutput("all")
)
)
)
server <- function(input, output, session) {
plot_data1 <- reactive({
subset(data_mean_sd1, Gene %in% input$selectGeneSymbol)
})
plot_data2 <- reactive({
subset(data_mean_sd1, Gene %in% input$selectGeneSymbol2)
})
global <- reactiveValues(out = NULL)
observeEvent(input$plot1, {
global$out <- plotOutput("plot1", height=750)
})
observeEvent(input$plot2, {
global$out <- plotOutput("plot2", height=750)
})
observeEvent(input$all, {
global$out <- plotOutput("plot3", height=1150)
})
output$all <- renderUI({
global$out
})
p1 <- eventReactive(list(input$plot1,
input$all), {
ggplot(data = plot_data1(), aes(x = Name, y = mean,fill=Name)) +
geom_bar(stat = "identity", position = position_dodge(0.9), width = 0.9) +
geom_errorbar(aes(ymin = mean - sd, ymax = mean + sd), width = .2, position = position_dodge(0.9)) +
theme_classic2() +
rotate_x_text(angle = 45) +
theme(legend.position = "none") +
labs(title = input$selectGeneSymbol, x = NULL, y = "FPKM_value") +
theme(plot.title = element_text(hjust = 0.5)) +
theme(plot.margin = unit(c(20, 5, 1, 5), "mm"))+
theme(axis.text.x=element_text(vjust=1,size=12))
})
p2 <- eventReactive(list(input$plot2,
input$all), {
ggplot(data = plot_data2(), aes(x = Name, y = mean,fill=Name)) +
geom_bar(stat = "identity", position = position_dodge(0.9), width = 0.9) +
geom_errorbar(aes(ymin = mean - sd, ymax = mean + sd), width = .2, position = position_dodge(0.9)) +
theme_classic2() +
rotate_x_text(angle = 45) +
theme(legend.position = "none") +
labs(title = input$selectGeneSymbol2, x = NULL, y = "FPKM_value") +
theme(plot.title = element_text(hjust = 0.5)) +
theme(plot.margin = unit(c(20, 5, 1, 5), "mm"))+
theme(axis.text.x=element_text(vjust=1,size=12))
})
#plotlist <- do.call(tagList, list(p1(),p2()))
output$plot1 <- renderPlot({ p1() })
output$plot2 <- renderPlot({ p2() })
output$plot3 <- renderPlot({ grid.arrange(p1(),p2(), ncol=1) })
observe({
plots <- list(p1(),p2())
myplots <- do.call(grid.arrange, c(plots, ncol = 1))
output$fp_save <- downloadHandler(
filename = function() {
paste("myplots", tolower(input$fp_device), sep = ".")
},
content = function(file) {
ggsave(file, plot=myplots, device = tolower(input$fp_device))
}
)
})
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)

checkboxInput value R shiny: if TRUE then

I have the following code. The objective is to make the position of the plot bars reactive to the selectInput value
library(shiny)
library(shinyWidgets)
library(tidyverse)
library(DT)
library(shinythemes)
library(plotly)
library(ggthemes)
library(lubridate)
data <- data.frame(mitarbeiter = c("AA", "BB", "CC", "DD", "EE", "FF"),
art = c("hr", "GG", "TT", "RR", "OO", "OO"),
creadate = as_date(c("2018-01-03", "2018-01-03", "2018-01-03", "2018-01-03", "2018-01-03", "2018-01-03")))
mitarbeiter1 <- sort(unique(data$mitarbeiter))
art1 <- sort(unique(data$art))
year_month <- function(dates) {
paste(lubridate::year(dates),
str_pad(lubridate::month(dates), width = 2, pad = 0),
sep="-")
}
year_week <- function(dates) {
paste(lubridate::year(dates),
str_pad(lubridate::week(dates), width = 2, pad = 0),
sep="-")
}
year_day <- function(dates) {
paste(lubridate::year(dates),
str_pad(lubridate::month(dates), width = 2, pad = 0),
str_pad(lubridate::day(dates), width = 2, pad = 0),
sep="-")
}
ui <- fluidPage(
fluidRow(
column(4,
pickerInput("mitarbeiterName", "Name des Mitarbeiters", mitarbeiter1,
options = list(`actions-box` = TRUE), multiple = TRUE),
pickerInput("artName", "Art", art1,
options = list(`actions-box` = TRUE), multiple = TRUE),
pickerInput("period", "Zeitraum", c("day", "week", "month", "year"),
options = list(`actions-box` = TRUE)),
dateRangeInput("date", "Datum auswahlen", start = "2020-01-01"),
checkboxInput("kumulativ", "Kumulativ"),
downloadButton("download", "Download")
),
column(8,
plotlyOutput("policyPlot")
)
)
)
server <- function(input, output, session) {
#create a reactive object with a NULL starting value
listofrows <- reactiveValues(data = NULL)
#observe the changes in inputs and update the reactive object
observeEvent(c(input$mitarbeiterName, input$artName, input$date, input$period), {
req(input$mitarbeiterName)
req(input$artName)
req(input$period)
req(input$date)
listofrows$data <- subset(data, mitarbeiter %in% input$mitarbeiterName &
art %in% input$artName &
creadate >= input$date[1] & creadate <= input$date[2])
}, ignoreInit = T, ignoreNULL = TRUE)
output$policyPlot <- renderPlotly({
req(listofrows$data)
req(input$kumulativ)
fn <- switch(
input$period,
day = year_day,
week = year_week,
month = year_month,
year = year
)
pos <- if (input$kumulativ) "dodge" else "identity"
ggplot(listofrows$data) +
geom_bar(aes(x = fn(creadate), fill = mitarbeiter),
stat = "count",
position = pos,
show.legend = T) +
ggtitle("Anzahl erstellte Policen (pro Mitarbeiter)") +
xlab("Zeitraum") + ylab("Anzahl der Policen")
})
output$download <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".png", sep = "")
},
content = function(file) {
ggsave(file, plot = output$policyPlot)
})
}
shinyApp(ui, server)
Now, I want:
the position to be "dodge" if checkboxInput = TRUE, and
the position to be "identity" if checkboxInput = FALSE.
does someone have any suggestion how to do that? How can we do the if condition with the checkbox value?
In your case, req(input$kumulativ) doesn't work. It's because req checks if a value is "truthy", and FALSE is not considered truthy. Therefore, you can change it to:
req(!is.null(input$kumulativ))

How can I get both widgets in my shiny code to work simultaneously?

Currently, I am working on creating an interactive graph using shiny. My ui.R file contains two widgets, a checkbox and a selectInput:
checkboxGroupInput(inputId = "checkbox",
label = h3("Education Level"),
choices = c("Bachelor's Degree" = 'Bachelor',
"Master's Degree" = 'Master'
),
),
selectInput(inputId = "select",
label = h3("Gender"),
choices = c("Female" = 'F',
"Male" = 'M',
"Both" = 'B'
),
)
In my server.R file, I am able to get my selectInput to successfully run. However, my checkbox is currently not working. Here is an example:
server <- function(input, output) {
output$locksley_plot <- renderPlot({
choice_button <- input$select
check_box <- input$checkbox
BM <- ggplot(data = tech_salaries_bachelor) +
geom_point(
mapping = aes(x = Education, y = Male, color = "blue")
) +
scale_color_manual(labels = c("Men"), values=c("blue")) +
labs(
title = "Tech Salary Gender Comparison",
x = "Education Level",
y = "Salary ($)"
)
if(choice_button == 'M' && check_box == 'Bachelor') {
return(BM)
}
})
}
shinyServer(server)
#Locksley Here is an example shiny app that uses both widgets. I'm not entirely sure what outcome you are looking for, but thought this might be helpful.
tech_salaries_bachelor <- data.frame(
Education = c("Bachelor", "Bachelor", "Master", "Master", "Bachelor"),
Salary = c(30000, 35000, 55000, 50000, 27000),
Gender = c("F", "F", "F", "M", "M")
)
library(shiny)
library(tidyverse)
ui <- fluidPage(
checkboxGroupInput(inputId = "checkbox",
label = h3("Education Level"),
choices = c("Bachelor's Degree" = 'Bachelor',
"Master's Degree" = 'Master'
),
),
selectInput(inputId = "select",
label = h3("Gender"),
choices = c("Female" = 'F',
"Male" = 'M',
"Both" = 'B'
),
),
plotOutput("locksley_plot")
)
server <- function(input, output, session) {
my_data <- reactive({
req(input$checkbox)
tech_salaries_bachelor %>%
filter(Education %in% input$checkbox,
if(input$select != 'B') (Gender == input$select) else TRUE) %>%
group_by(Education, Gender) %>%
dplyr::summarise(Mean_Salary = mean(Salary))
})
output$locksley_plot <- renderPlot({
ggplot(data = my_data(), aes(x = Education, y = Mean_Salary, fill = Gender)) +
geom_bar(stat = "identity", position = position_dodge()) +
scale_fill_manual(labels = c("F" = "Female", "M" = "Male"), values=c("F" = "pink", "M" = "blue")) +
labs(
title = "Tech Salary Gender Comparison",
x = "Education Level",
y = "Average Salary ($)"
)
})
}
shinyApp(ui, server)

Resources