shiny click on plot update input - r

I have this very simple shiny app
When input changes, the graph changes accordingly
When a point is selected within the graph the corresponding model is displayed on the right of the input text box
I would like to see the selection to be displayed inside the text box
Can anyone please point me in the right direction
Thanks for any help
require(ggplot2)
require(dplyr)
require(Cairo)
require(dplyr)
mtcars2 <- datasets::mtcars %>% rownames_to_column(var = 'model')
ui <- fluidPage(
fluidRow(
column(width = 3,
selectInput(inputId = '.model', label = 'input model', choices = mtcars2$model, selected = NULL)),
br(),br(),
column(width = 3,
textOutput('click_1A'), label = 'selected model')
),
fluidRow(
column(width = 8,
plotOutput("plot1", height = 350, brush = "plot_brush", click = "plot_click")),
)
)
server <- function(input, output) {
global <- reactiveValues(.model = NULL)
# scatter plot
output$plot1 <- renderPlot({
selected_model <- input$.model
ggplot(mtcars2, aes(x=mpg,y=disp), color = 'red') +
geom_point(size = 3, col = 'red') +
geom_point(data = filter(mtcars2, model == selected_model), col = 'blue', size = 5) +
theme_bw() +
theme(legend.position = 'none')
})
# MODEL name
output$click_1A <- renderText({
near_out <- nearPoints(mtcars2, input$plot_click, addDist = TRUE)
global$.model <- near_out %>%
pull(model)
})
}
shinyApp(ui, server)

Thanks #Ben
Here is the clean version of what was trying to achieve:
require(ggplot2)
require(tidyr)
require(tibble)
require(lubridate)
require(Cairo)
require(dplyr)
mtcars2 <- datasets::mtcars %>% rownames_to_column(var = 'model')
ui <- fluidPage(
fluidRow(
column(width = 3,
selectInput(inputId = '.model', label = 'input model', choices = mtcars2$model, selected = NULL)),
),
fluidRow(
column(width = 8,
plotOutput("plot1", height = 350, brush = "plot_brush", click = "plot_click")),
)
)
server <- function(input, output, session) {
global <- reactiveValues(.model = NULL)
# scatter plot
output$plot1 <- renderPlot({
selected_model <- input$.model
ggplot(mtcars2, aes(x=mpg,y=disp, label = model), color = 'red') +
geom_point(size = 3, col = 'red') +
geom_point(data = filter(mtcars2, model == selected_model), col = 'blue', size = 5) +
#geom_text() +
theme_bw() +
theme(legend.position = 'none')
})
observeEvent(
eventExpr = input$plot_click,
handlerExpr = {
selected_model <- nearPoints(mtcars2, input$plot_click, maxpoints = 1, addDist = F) %>% pull(model)
updateSelectInput(session, inputId = ".model", choices = mtcars2$model, selected = selected_model)}
)
}
shinyApp(ui, server)

Related

R Shiny recoloring of points

I would like click-select points and group them based on color.
I can save selected points with color information into a new data frame and plot it, however I would like to keep track and see what was already selected on the interactive plot.
How can I show/label already selected points or make it permanent after "Add selection"?
library(shiny)
library(tidyverse)
library(DT)
library(colourpicker)
ui = fluidPage(
colourInput("col", "Select colour", "purple"),
actionButton("addToDT", "Add selection", icon = icon("plus")), hr(),
plotOutput("plot", click = "plot_click", dblclick = "plot_reset"),
DT::dataTableOutput('plot_DT'), hr(),
textOutput("clickcoord"),
DT::dataTableOutput('final_DT'),
plotOutput("plotSelected")
)
server = function(input, output, session) {
selectedPoint = reactiveVal(rep(FALSE, nrow(mtcars)))
output$clickcoord <- renderPrint({
print(input$plot_click)
})
observeEvent(input$plot_click, {
clicked = nearPoints(mtcars, input$plot_click, allRows = TRUE)$selected_
selectedPoint(clicked | selectedPoint())
})
observeEvent(input$plot_reset, {
selectedPoint(rep(FALSE, nrow(mtcars)))
})
output$plot_DT = DT::renderDataTable({
mtcars$sel = selectedPoint()
mtcars = dplyr::filter(mtcars, sel == TRUE) %>% mutate(group_color = input$col)
})
final_DT = reactiveValues()
final_DT$df = data.frame()
FinalData = eventReactive(input$addToDT, {
mtcars$sel = selectedPoint()
mtcars = dplyr::filter(mtcars, sel == TRUE) %>% mutate(group_color = input$col)
final_DT$df = bind_rows(final_DT$df, mtcars)
})
output$final_DT = renderDataTable({FinalData()})
output$plot = renderPlot({
mtcars$sel = selectedPoint()
ggplot(mtcars, aes(wt, mpg, color = mtcars$sel, fill=mpg)) +
geom_point(shape = 21, size = 6, stroke = 2) +
scale_color_manual(values = c("#ffffff00", input$col)) +
scale_fill_viridis_c() +
theme_bw()
})
output$plotSelected = renderPlot({
sel_df = FinalData()
ggplot(sel_df, aes(wt, mpg, fill = group_color, colour = group_color)) +
geom_point(shape = 21, size = 6, stroke = 2) +
scale_color_manual(values = unique(sel_df$group_color)) +
scale_fill_manual(values = unique(sel_df$group_color)) +
theme_bw()
})
observeEvent(input$addToDT, {
selectedPoint(rep(FALSE, nrow(mtcars)))
})
}
shinyApp(ui, server)
I think this is the "crux" of what your are looking for. I used a very similar example that I found in the help for entitled:
A demonstration of clicking, hovering, and brushing
(https://shiny.rstudio.com/reference/shiny/0.13.1/plotoutput)
It is very similar to your example.
I create a matrix of T/F elements where the rows are the observations and the columns are in which batch the observation is selected. So when you launch the whole matrix is False, but as you click on observations the switch to positive in the first column. Then if you click addSelection and continue you start switching the observations in the next column.
Could you confirm that this what you are looking for?
Below is the code.
shinyApp(
ui = basicPage(
fluidRow(
column(
width = 4,
plotOutput("plot",
height = 300,
click = "plot_click", # Equiv, to click=clickOpts(id='plot_click')
),
actionButton("addToDT", "Add selection", icon = icon("plus")), hr(),
h4("Clicked points"),
tableOutput("plot_clickedpoints"),
),
column(
width = 4,
verbatimTextOutput("counter"),
),
)
),
server = function(input, output, session) {
data <- reactive({
input$newplot
# Add a little noise to the cars data so the points move
cars + rnorm(nrow(cars))
})
output$plot <- renderPlot({
d <- data()
plot(d$speed, d$dist, main = paste("No of Sets Chosen", input$addToDT))
})
output$plot_clickinfo <- renderPrint({
cat("Click:
")
str(input$plot_click)
})
selectedPoints <- reactiveVal(rep(FALSE, nrow(cars)))
selectionMatrix <- reactiveVal(matrix(data = F, nrow = nrow(cars), ncol = 7))
observeEvent(input$plot_click, {
clicked <- nearPoints(data(), input$plot_click, "speed", "dist", allRows = TRUE)$selected
selectedPoints(clicked | selectedPoints())
tmp <- unlist(selectionMatrix())
tmp[, (input$addToDT + 1)] <- selectedPoints()
selectionMatrix(tmp)
})
observeEvent(input$addToDT, {
selectedPoints(rep(FALSE, nrow(cars)))
})
output$plot_clickedpoints <- renderTable({
# if (input$addToDT==0) {
res <- selectionMatrix()
return(res)
})
}
)

Shiny app very slow and RegData error, what in code might be causing this?

I'm trying to run a dashboard on shiny and the end result is incredibly slow, I'm also getting an error when running so i suspect the app might have crashed? any idea what in the code might be causing this?
I just start to code the linear regression I'm not sure about the efficiently of the code.
The plots not appears on the dashboard.
Thank youu
# Define UI ----
ui <- fluidPage(
titlePanel("AirBnb NYC"),
sidebarLayout(
sidebarPanel(
fluidRow(
column(3,
selectInput("select", h3("Which Neighbourhood group ?"), choices =
c("Brooklyn","Manhattan","Queens","Staten Island", "Bronx"))),
column(3,
selectInput("select2", h3("which Neighbourhood ?"), choices = "")),
column(3,
selectInput("select1", h3("Room Type"), choices = ""))),
p("Select the inputs for the Dependent Variable"),
selectInput(inputId = "DepVar", label = "Dependent Variables", multiple = FALSE, choices =
colnames(AB_NYC_2019)),
p("Select the inputs for the Independent Variable"),
selectInput(inputId = "IndVar", label = "Independent Variables", multiple = FALSE,
choices = list( "price"))
),
mainPanel( leafletOutput("map",width = "100%",height = "800"),
fluidRow(column(width = 6, plotOutput("data")),
column(width = 6, plotOutput("plot"))),
verbatimTextOutput(outputId = "RegSum"),
verbatimTextOutput(outputId = "IndPrint"),
verbatimTextOutput(outputId = "DepPrint"))
))
Define server logic ----
server <- function(input, output, session) {
#Define parameters of search
observe({
print(input$select)
x <- AB_NYC_2019 %>% filter(neighbourhood_group == input$select) %>% select(neighbourhood)
updateSelectInput(session, "select2", "select your neighbourhood", choice = unique(x))
})
observe({
productdata <- AB_NYC_2019$room_type[AB_NYC_2019$neighbourhood == input$select2]
updateSelectInput(session, "select1", "Which room type?", choices = unique(productdata))
})
#Create map
color <- colorFactor(palette = c("red", "green", "blue", "purple", "yellow"),
AB_NYC_2019$neighbourhood_group)
filteredData <- reactive({filter(AB_NYC_2019, neighbourhood_group == input$select)})
output$map <- renderLeaflet({
map <- leaflet(filteredData()) %>% addProviderTiles(providers$Esri.WorldTopoMap) %>%
# setView(lng = -73.98928, lat = 40.75042, zoom = 10) %>%
addCircleMarkers(
lng=~longitude, # Longitude coordinates
lat=~latitude, # Latitude coordinates
stroke=TRUE, # Circle stroke
weight = 0.1,
radius = 0.5,
fillOpacity=0.5,
color=~color(neighbourhood_group),
label = paste("Name:", AB_NYC_2019$name, "<br>",
"Price:", AB_NYC_2019$price, "<br>",
"Reviews:", AB_NYC_2019$number_of_reviews, "<br>")) %>%
addLegend("bottomright", pal = color, values = ~neighbourhood_group,
title = "Neighbourhood groups",
opacity = 1
)
})
#filter map
observe({
leafletProxy("map", data = filteredData()) %>%
clearShapes() %>%
addMarkers(~longitude, ~latitude,
label = ~neighbourhood_group,
labelOptions = labelOptions(textsize = "12px"))
})
lm1 <- reactive({lm(reformulate(input$IndVar, input$DepVar), data = RegData)})
output$DepPrint <- renderPrint({input$DepVar})
output$IndPrint <- renderPrint({input$IndVar})
output$RegSum <- renderPrint({summary(lm1())})
#Get many plots
output$data <- renderPlot({
ggplot(AB_NYC_2019, aes(price)) +
geom_histogram(bins = 30, aes(y = ..density..), fill = "purple") +
geom_density(alpha = 0.2, fill = "purple") +
th +
ggtitle("Distribution of price",
subtitle = "The distribution is very skewed") +
theme(axis.title = element_text(), axis.title.x = element_text()) +
geom_vline(xintercept = round(mean(AB_NYC_2019$price), 2), size = 2, linetype = 3)
})
output$plot <- renderPlot({
AB_NYC_2019 %>% filter(price >= mean(price)) %>% group_by(neighbourhood_group, room_type) %>%
tally %>%
ggplot(aes(reorder(neighbourhood_group,desc(n)), n, fill = room_type)) +
th +
xlab(NULL) +
ylab("Number of objects") +
ggtitle("Number of above average price objects",
subtitle = "Most of them are entire homes or apartments") +
geom_bar(stat = "identity")
})
}
Run the app ----
shinyApp(ui = ui, server = server)
Try this
AB_NYC_2019 <- AB_NYC_2019[1:50,]
# Define UI ----
ui <- fluidPage(
titlePanel("AirBnb NYC"),
sidebarLayout(
sidebarPanel(
selectInput("select", h3("Which Neighbourhood group ?"), choices =
c("Brooklyn","Manhattan","Queens","Staten Island", "Bronx")),
selectInput("select2", h3("which Neighbourhood ?"), choices = ""),
selectInput("select1", h3("Room Type"), choices = ""),
p("Select the inputs for the Dependent Variable"),
selectInput(inputId = "DepVar", label = "Dependent Variables", multiple = FALSE, choices =
colnames(AB_NYC_2019)),
p("Select the inputs for the Independent Variable"),
selectInput(inputId = "IndVar", label = "Independent Variables", multiple = FALSE,
choices = list( "price"))
),
mainPanel( leafletOutput("map",width = "100%",height = "800"),
fluidRow(column(width = 6, plotOutput("data")),
column(width = 6, plotOutput("plot"))),
verbatimTextOutput(outputId = "RegSum"),
verbatimTextOutput(outputId = "IndPrint"),
verbatimTextOutput(outputId = "DepPrint"))
)
)
server <- function(input, output, session) {
#Define parameters of search
observe({
req(input$select)
x <- AB_NYC_2019 %>% dplyr::filter(neighbourhood_group == input$select) %>% select(neighbourhood)
updateSelectInput(session, "select2", "select your neighbourhood", choice = unique(x))
})
observe({
req(input$select2)
productdata <- AB_NYC_2019$room_type[AB_NYC_2019$neighbourhood == input$select2]
updateSelectInput(session, "select1", "Which room type?", choices = unique(productdata))
})
#Create map
color <- colorFactor(palette = c("red", "green", "blue", "purple", "yellow"),
unique(AB_NYC_2019$neighbourhood_group))
filteredData <- reactive({
req(input$select)
filter(AB_NYC_2019, neighbourhood_group == input$select)})
output$map <- renderLeaflet({
req(filteredData())
map <- leaflet(filteredData()) %>% addProviderTiles(providers$Esri.WorldTopoMap) %>%
setView(lng = -73.98928, lat = 40.75042, zoom = 10) %>%
addCircleMarkers(
lng=~longitude, # Longitude coordinates
lat=~latitude, # Latitude coordinates
stroke=TRUE, # Circle stroke
weight = 0.1,
radius = 0.5,
fillOpacity=0.5,
color=~color(neighbourhood_group),
label = paste("Name:", filteredData()$name,
"\nPrice:", filteredData()$price,
"\nReviews:", filteredData()$number_of_reviews)
) %>%
addLegend("bottomright", pal = color, values = ~neighbourhood_group,
title = "Neighbourhood groups",
opacity = 1
)
})
#filter map
observe({
req(filteredData())
labs <- lapply(seq(nrow(filteredData())), function(i) {
paste0( '<p> Name: ', filteredData()[i, "name"], '<p></p>', 'Price: ',
filteredData()[i, "price"],'</p><p>', 'Reviews: ',
filteredData()[i, "number_of_reviews"], '</p>' )
})
leafletProxy("map", data = filteredData()) %>%
clearShapes() %>%
addMarkers(~longitude, ~latitude,
label = lapply(labs, htmltools::HTML),
labelOptions = labelOptions(textsize = "12px"))
})
lm1 <- reactive({
req(filteredData())
lm(reformulate(input$IndVar, input$DepVar), data = filteredData())})
output$DepPrint <- renderPrint({input$DepVar})
output$IndPrint <- renderPrint({input$IndVar})
output$RegSum <- renderPrint({
req(lm1())
summary(lm1())})
#Get many plots
output$data <- renderPlot({
ggplot(AB_NYC_2019, aes(price)) +
geom_histogram(bins = 30, aes(y = ..density..), fill = "purple") +
geom_density(alpha = 0.2, fill = "purple") +
theme_bw() +
ggtitle("Distribution of price",
subtitle = "The distribution is very skewed") +
theme(axis.title = element_text(), axis.title.x = element_text()) +
geom_vline(xintercept = round(mean(AB_NYC_2019$price), 2), size = 2, linetype = 3)
})
output$plot <- renderPlot({
AB_NYC_2019 %>% filter(price >= mean(price)) %>% group_by(neighbourhood_group, room_type) %>%
tally %>%
ggplot(aes(reorder(neighbourhood_group,desc(n)), n, fill = room_type)) +
theme_bw() +
xlab(NULL) +
ylab("Number of objects") +
ggtitle("Number of above average price objects",
subtitle = "Most of them are entire homes or apartments") +
geom_bar(stat = "identity")
})
}
shinyApp(ui, server)

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)

Hide boxes if input not suitable in Shiny

I am using shiny and shinydashboard. There are a couple of instances when I would like all or most boxes/plots to be hidden.
If the date range is impossible (i.e. the end date is earlier than the start date).
If inputs selected make the sample size too small.
With issue 1, I want to hide all the boxes and just return an error message. With issue 2, I'd like to show a few infoboxes at the top (e.g. sample size), but hide all the rest of the boxes.
Currently, I am producing an error message using validate for the first condition, and also using validate to stop the plots from running when this happens. However, this still leaves the boxes, even though they are empty, which is quite ugly and messy.
I would probably be able to put every box into a conditionalPanel, I guess, but that seems very repetitive - surely there is a simpler way to pass an argument to all (or a group of) boxes? This code is an example - there are a lot more boxes in the app I am working on.
Example code:
library(shiny)
library(shinydashboard)
library(tidyverse)
random_data <- data.frame(replicate(2, sample(0:10, 1000, rep=TRUE)))
set.seed(1984)
random_data$date <- sample(seq(as.Date('2016-01-01'), as.Date(Sys.Date()), by = "day"), 1000)
sidebar <- dashboardSidebar(dateRangeInput(
"dates", label = h4("Date range"), start = '2016-01-01', end = Sys.Date(),
format = "dd-mm-yyyy", startview = "year", min = "2016-01-01", max = Sys.Date()
))
body <- dashboardBody(
textOutput("selected_dates"),
br(),
fluidRow(
infoBoxOutput("total", width = 12)
),
fluidRow(
box(width = 12, solidHeader = TRUE,
title = "X1 over time",
plotOutput(outputId = "x1_time")
)
),
fluidRow(
box(width = 12, solidHeader = TRUE,
title = "X2 over time",
plotOutput(outputId = "x2_time")
)
)
)
ui <- dashboardPage(dashboardHeader(title = "Example"),
sidebar,
body
)
server <- function(input, output) {
filtered <- reactive({
filtered_data <- random_data %>%
filter(date >= input$dates[1] & date <= input$dates[2])
return(filtered_data)
})
output$selected_dates <- renderText({
validate(
need(input$dates[2] >= input$dates[1], "End date is earlier than start date"
)
)
})
output$total<- renderInfoBox({
validate(
need(input$dates[2] >= input$dates[1], "")
)
infoBox(title = "Sample size",
value = nrow(filtered()),
icon = icon("binoculars"), color = "light-blue")
})
output$x1_time <- renderPlot({
validate(
need(input$dates[2] >= input$dates[1], "")
)
x1_time_plot <- ggplot(filtered(), aes(x = date, y = X1)) +
geom_bar(stat = "identity")
theme_minimal()
x1_time_plot
})
output$x2_time <- renderPlot({
validate(
need(input$dates[2] >= input$dates[1], "")
)
x2_time_plot <- ggplot(filtered(), aes(x = date, y = X2)) +
geom_bar(stat = "identity")
theme_minimal()
x2_time_plot
})
}
shinyApp(ui, server)
You could use shinyjs and the show/hide method on all the inputIds that you want to hide or show or you can put all the boxes in a div with a class and use the hide/show with this class or assign a class directly to the fluidRows.
With both examples validate+need is not required anymore.
This example shows/hides the individual output IDs:
library(shiny)
library(shinydashboard)
library(tidyverse)
library(shinyjs)
## DATA ##################
random_data <- data.frame(replicate(2, sample(0:10, 1000, rep=TRUE)))
set.seed(1984)
random_data$date <- sample(seq(as.Date('2016-01-01'), as.Date(Sys.Date()), by = "day"), 1000)
sidebar <- dashboardSidebar(dateRangeInput(
"dates", label = h4("Date range"), start = '2016-01-01', end = Sys.Date(),
format = "dd-mm-yyyy", startview = "year", min = "2016-01-01", max = Sys.Date()
))
##################
## UI ##################
body <- dashboardBody(
useShinyjs(),
textOutput("selected_dates"),
br(),
fluidRow(
infoBoxOutput("total", width = 12)
),
fluidRow(
box(width = 12, solidHeader = TRUE,
title = "X1 over time",
plotOutput(outputId = "x1_time")
)
),
fluidRow(
box(width = 12, solidHeader = TRUE,
title = "X2 over time",
plotOutput(outputId = "x2_time")
)
)
)
ui <- dashboardPage(dashboardHeader(title = "Example"),
sidebar,
body
)
##################
server <- function(input, output) {
filtered <- reactive({
filtered_data <- random_data %>%
filter(date >= input$dates[1] & date <= input$dates[2])
return(filtered_data)
})
observe({
if (input$dates[2] < input$dates[1]) {
shinyjs::hide("total")
shinyjs::hide("x1_time")
shinyjs::hide("x2_time")
} else {
shinyjs::show("total")
shinyjs::show("x1_time")
shinyjs::show("x2_time")
}
})
output$total<- renderInfoBox({
infoBox(title = "Sample size",
value = nrow(filtered()),
icon = icon("binoculars"), color = "light-blue")
})
output$x1_time <- renderPlot({
x1_time_plot <- ggplot(filtered(), aes(x = date, y = X1)) +
geom_bar(stat = "identity")
theme_minimal()
x1_time_plot
})
output$x2_time <- renderPlot({
x2_time_plot <- ggplot(filtered(), aes(x = date, y = X2)) +
geom_bar(stat = "identity")
theme_minimal()
x2_time_plot
})
}
shinyApp(ui, server)
This example uses classes for the fluidRows, so this will hide the whole main page of the dashboard:
## UI ##################
body <- dashboardBody(
useShinyjs(),
textOutput("selected_dates"),
br(),
fluidRow(class ="rowhide",
infoBoxOutput("total", width = 12)
),
fluidRow(class ="rowhide",
box(width = 12, solidHeader = TRUE,
title = "X1 over time",
plotOutput(outputId = "x1_time")
)
),
fluidRow(class ="rowhide",
box(width = 12, solidHeader = TRUE,
title = "X2 over time",
plotOutput(outputId = "x2_time")
)
)
)
ui <- dashboardPage(dashboardHeader(title = "Example"),
sidebar,
body
)
##################
server <- function(input, output) {
filtered <- reactive({
filtered_data <- random_data %>%
filter(date >= input$dates[1] & date <= input$dates[2])
return(filtered_data)
})
observe({
if (input$dates[2] < input$dates[1]) {
shinyjs::hide(selector = ".rowhide")
} else {
shinyjs::show(selector = ".rowhide")
}
})
output$total<- renderInfoBox({
infoBox(title = "Sample size",
value = nrow(filtered()),
icon = icon("binoculars"), color = "light-blue")
})
output$x1_time <- renderPlot({
x1_time_plot <- ggplot(filtered(), aes(x = date, y = X1)) +
geom_bar(stat = "identity")
theme_minimal()
x1_time_plot
})
output$x2_time <- renderPlot({
x2_time_plot <- ggplot(filtered(), aes(x = date, y = X2)) +
geom_bar(stat = "identity")
theme_minimal()
x2_time_plot
})
}
shinyApp(ui, server)

How can I customise the text output in a verbatimTextOuput function within a Shiny interactive plot in R

I want to change the text output to show specific information about the plot point being hovered over such as name and value and such, but I cant seem to workout how to alter the VerbatimTextOutput and what it currently show is not of much use for a quick glace of information. This is the code I am using:
```
ui <- fluidPage(
fluidRow(
column(width = 8, class = "well",
h4("Brush to select region. Left plot controls right plot"),
fluidRow(
column(width = 12,
plotOutput("plot2", height = 300,
brush = brushOpts(
id = "plot2_brush",
resetOnNew = TRUE
)
)
),
column(width = 12,
plotOutput("plot3", height = 300, hover = hoverOpts(id = "plot_hover"))
)
)
),
column(width = 3,
verbatimTextOutput("hover_info", placeholder = TRUE))
)
)
server <- function(input, output) {
# Linked plots (middle and right)
ranges2 <- reactiveValues(x = NULL, y = NULL)
output$plot2 <- renderPlot({
ggplot(adj_p_val_df, aes(x = Genes, y = p_value, col = Model))+
geom_point()+
theme(legend.position = "none")
})
output$plot3 <- renderPlot({
ggplot(adj_p_val_df, aes(x = Genes, y = p_value, col = Model))+
geom_point() +
coord_cartesian(xlim = ranges2$x, ylim = ranges2$y, expand = FALSE)
})
output$hover_info = renderPrint({
cat("Gene info:\n")
str(input$plot_hover)
})
output$placeholder = renderTe
# When a double-click happens, check if there's a brush on the plot.
# If so, zoom to the brush bounds; if not, reset the zoom.
observe({
brush <- input$plot2_brush
if (!is.null(brush)) {
ranges2$x <- c(brush$xmin, brush$xmax)
ranges2$y <- c(brush$ymin, brush$ymax)
} else {
ranges2$x <- NULL
ranges2$y <- NULL
}
})
}
shinyApp(ui, server)
```

Resources