Can we bind three plots using rbind_gtable in shiny dashboard to download all the plots together? - ggplot2

Trouble combining multiple shiny plots and downloading through a single click.
Code below is from the answer here: Solution
I tried this solution. It works fine for two plots but as soon as I add another plot it only returns the graph information but not the graph themselves.
Combining two plots works fine.
I also tried other solutions but both of the solutions when implemented returns a text file instead of pdf or pdf file that is corrupted.
Solution1
Solution2
Any suggestion would be really appreciated. Thank you!
Code
library(ggplot2)
ui <- shinyUI(fluidPage(
titlePanel("Test app"),
fluidRow(
column(4,
wellPanel(
downloadButton('download',label="Download plot as png")
)
),
column(8,
plotOutput("plot")
)
)
))
server <- function(input,output) {
plotting<- reactive({
data1=data.frame(x=rnorm(50),y=rnorm(50))
data2=data.frame(x=rexp(50),y=rexp(50))
data3=data.frame(x=rexp(50),y=rexp(50))
plot1=ggplot(data1,aes(x,y))+geom_point()
plot2=ggplot(data2,aes(x,y))+geom_point()
plot3=ggplot(data3,aes(x,y))+geom_point()
gb1=ggplot_build(plot1)
gb2=ggplot_build(plot2)
gb3=ggplot_build(plot3)
gA <- ggplot_gtable(gb1)
gB <- ggplot_gtable(gb2)
gC <- ggplot_gtable(gb3)
both <- gtable:::rbind_gtable(gA, gB, "last")
all <- gtable:::rbind_gtable(both, gC, "last")
return(all)
})
output$plot <- renderPlot({
grid.newpage()
grid.draw(plotting())
})
output$download <- downloadHandler(
filename <- "shinytestplot.png",
# Changes:
content <- function(file){ ## file = NULL --> file
png(file) # filename --> file
grid.newpage()
grid.draw(plotting())
dev.off()
}
)
}
shinyApp(server=server,ui=ui)

Your example works just fine for me. However, I'd recommend against rbinding plots. Use the patchwork package instead.
library(ggplot2)
library(shiny)
library(patchwork)
ui <- shinyUI(fluidPage(
titlePanel("Test app"),
fluidRow(
column(4,
wellPanel(
downloadButton('download',label="Download plot as png")
)
),
column(8,
plotOutput("plot")
)
)
))
server <- function(input,output) {
plotting<- reactive({
data1=data.frame(x=rnorm(50),y=rnorm(50))
data2=data.frame(x=rexp(50),y=rexp(50))
data3=data.frame(x=rexp(50),y=rexp(50))
plot1=ggplot(data1,aes(x,y))+geom_point()
plot2=ggplot(data2,aes(x,y))+geom_point()
plot3=ggplot(data3,aes(x,y))+geom_point()
# stack the plots on top of one another with patchwork
plot1 / plot2 / plot3
})
output$plot <- renderPlot({
print(plotting())
})
output$download <- downloadHandler(
filename <- "shinytestplot.png",
# Changes:
content <- function(file){
ggsave(file, plotting())
}
)
}
shinyApp(server=server,ui=ui)

Related

How to modify ggboxplot (ggpubr) to suppress whiskers, but retain access to other boxplot customisations?

Originally I asked this question about suppressing the whiskers on a boxplot made by ggboxplot. (The expected way of setting a geom_boxplot option was not available.) A nice solution appeared which suited the original question. However, the broader question to address is how to suppress whiskers on the boxplot but still retain access to the nice additions in ggpubr, such as being able to automatically compute statistical test results and place these on a boxplot.
I tinkered with the solution from #Julian_Hn to get something like what I want.
There are two issues that someone more knowledgeable might be able to help with, now that I've asked the broader question:
Are there ways to make the solution more efficient?
How can I add in the ability to change the range of x-values? (I tried various methods using ggpar and coord_cartesian, with no effect. I might be lacking knowledge of how to use commands like ggplot_build effectively.)
Here's an example where I suppress whiskers and use stat_kruskal_test to label the boxplot:
ggboxplot_whisker_opt <- function(...)
{
opts <- list(...) # Modification of original question solution to include the original labelled ggboxplot with whiskers and stat info added
# Check if user specified a whiskers arg and set options accordingly
if("whisker" %in% names(opts))
{
whisk <- opts$whisker
opts$whisker <- NULL
} else {
whisk <- TRUE
}
# Additional arguments that might need generalising so that other statistical tests can be used in other applications
if ("kruskal" %in% names(opts))
{ kruskal<-opts$kruskal
opts$kruskal <- NULL
opt.group <- opts$kruskal.options[[1]]
opt.label<- opts$kruskal.options[[2]]
opt.y <- opts$kruskal.options[[3]]
opt.x <- opts$kruskal.options[[4]]
opts$kruskal.options <- NULL
}
pl <- do.call(ggboxplot,opts) # create plot by calling ggboxplot with all user options
if (kruskal){ pl <- pl + stat_kruskal_test(group.by=opt.group,label=opt.label, label.y.npc=opt.y,label.x.npc=opt.x) }
if(!whisk)
{ pl_list <- ggplot_build(pl) # get listed version of ggboxplot object to modify
pl_list$data[[1]]$ymin <- NA # remove the ymin/max that specify the whiskers
pl_list$data[[1]]$ymax <- NA
pl <- ggplot_gtable(pl_list) # convert back to ggplot object
}
# return
pl
}
Here's the application:
set.seed(123)
x <-rnorm(100)
labels <- round(runif(100,1,2))
df <- data.frame(labels=labels, value=x)
# Define the options for the stat_kruskal_test label
KO <- list("group"="labels","label"="as_detailed_italic", "label.y.npc"=0.5,"label.x.npc"=0.5,ylim=c(-1.2, 1.2))
# call the function
output.plot <- ggboxplot_whisker_opt(df, "labels","value", col="labels", legend="none", whisker=FALSE,add=c("mean"), orientation="horizontal" kruskal=TRUE,kruskal.options=KO)
# Plot the result
plot(output.plot)
the issue with modifying was that the returned object was not a ggplot object anymore (wrong comment on my side) but a plot object. I have thought about it and instead of modifying the ggbuilt object, it's also possible to directly pass the coef=0 through to the geom_boxplot layer inside the object returned by ggboxplot:
ggboxplot_whisker_opt <- function(...)
{
opts <- list(...)
# check if user specified a whiskers argument and set options accordingly
if("whisker" %in% names(opts))
{
whisk <- opts$whisker
opts$whisker <- NULL
} else {
whisk <- TRUE
}
pl <- do.call(ggpubr::ggboxplot, opts) # create plot by calling ggboxplot with all user options
if(!whisk)
{
pl$layers[[1]]$stat_params$coef <- 0 # modify coef param of geom_boxplot layer
}
# plot the ggplot and return so other ggplot parts can be added via `+`
pl
}
This function now returns an object compatible with ggpar or adding other ggplot modifiers via +
library(ggplot2)
library(ggpubr)
set.seed(123)
x <- rnorm(100)
labels <- round(runif(100,1,2))
df <- data.frame(labels=labels,
value=x)
testplot <- ggboxplot_whisker_opt(df,"labels","value",whisker=FALSE)
ggpar(testplot,xlim=c(0.5,1.5),
ylim=c(-0.5,0.5))
testplot +
geom_line(data=data.frame(x=c(1,2),y=c(0,0)),aes(x=x,y=y),color="red",lwd=2)

What is the easiest way to add a tooltip to a boxplot(ggplot) generated through Shiny?

I need the y-value displayed in a tooltip-style text box when the cursor hovers over a bar. I have to imagine this is a simple function through Shiny, but I haven't been able to figure it out.
I'll include the relevant UI and server code below:
UI :
#TAB 3 (QUOTA/SALES)
tabPanel(title = "Quota/Sales",
fluidRow(column(9,
wellPanel(height=600, width ="100%",
plotOutput("quota", height=550, width ="100%"
))),
column(3,
wellPanel(height=100, width ="100%",
selectInput("countyquota", "County:", choices=countychoices, selected = "Statewide"))))
)))
Server:
hdata <- reactive({
if(input$sexage == "All" & input$countyharv == "Statewide") {
harvdata}
else if(input$sexage == "All" & input$countyharv != "Statewide")
{filter(harvdata, NAME == input$countyharv)}
else if (input$sexage != "All" & input$countyharv != "Statewide")
{filter(harvdata, sexage == input$sexage, NAME == input$countyharv)}
else if (input$countyharv == "Statewide" & input$sexage != "All"){
filter(harvdata, sexage == input$sexage)
}
})
output$harv <- renderPlot({
ggplot(hdata(), aes(fill=sexage, y=harvest, x=year, label = harvest)) +
geom_bar(position="dodge", stat="identity") +
xlab("Year") +
ylab("Harvest") +
labs(fill = NULL)+
theme_bw()
})`
Welcome to StackOverflow. The first thing is that it is always a good idea to include a minimal reproducible example of your base code. That way you help us to help you. In your case, the code you provide does not run (is not reporducible) you have to include the data (or a sample).
Going into the answer. ggplot2 output by default does not show tooltip, you need to use a JavaScript base library for that. The more common are plotly and highcharter.
Lets create a shiny app using the mtcars dataset to show you how to take ggplot2 plots to plotly using the plotly::ggplotly() function.
Note the important comments within the code.
library(shiny)
library(plotly) # you need this packages
ui <- fluidPage(
selectInput('x', 'X axis', choices = names(mtcars), selected = 'wt'),
selectInput('y', 'Y axis', choices = names(mtcars), selected = 'mpg'),
fluidRow(
column(
width = 6,
plotOutput('static'),
),
column(
width = 6,
# New function to render plotly outputs
plotlyOutput('dynamic')
)
)
)
server <- function(input, output, session) {
# This a regular ggplot2 object.
plot <- reactive({
ggplot(
data = mtcars,
mapping = aes(x = .data[[input$x]], y = .data[[input$y]])
) +
geom_point() +
theme_bw()
})
output$static <- renderPlot({ plot() })
# 1- New render frunction to handel plotly outputs
# 2- Place a ggplot2 object within the ggplotly() function
output$dynamic <- renderPlotly({ ggplotly(plot()) })
}
shinyApp(ui, server)

Flexdashboard not able to render ggplotly and ggplot object on same markdown

I have a basic reproducible example here that I think might just be a package limitation. I was wondering if I am just doing something wrong? They both plot fine separately but when combined in the same markdown make the dashboard unable to correctly render.
---
title: "Untitled"
output:
flexdashboard::flex_dashboard:
orientation: rows
source_code: embed
runtime: shiny
---
```{r setup, include=FALSE}
library(tidyverse)
library(plotly)
library(albersusa)
state_sf <- usa_sf("aeqd")
state_dat <- data.frame(state = c("Washington", "Wyoming","Texas","California"), pct = c(0.3,0.5,0.8,0.1))
state_map <- state_sf %>%
left_join(state_dat, by = c("name" = "state"))
```
Test
=====================================
Sidebar {.sidebar data-width=200}
-------------------------------------
Testing
Row
-----------------------------------------------------------------------
###Plotly
```{r graph 1, fig.height=4, fig.width=6}
#Symptoms by state last week===================================================
ggplotly(
ggplot(data = state_map) +
geom_sf(aes(fill=pct))
)
```
###Bar
```{r graph 2, fig.height=4, fig.width=3}
ggplot(data=state_dat) +
geom_col(aes(state,pct,fill=pct))
```
If you are using runtime: shiny you need to use the proper type of Shiny's renderX() functions for each type of plot object to display properly. I don't know why only one plot chunk (w/o renderX()) works, but two breaks it.
### Plotly
```{r graph_1, fig.height=4, fig.width=3}
#Symptoms by state last week
renderPlotly({
ggplotly(
ggplot(data = state_map) +
geom_sf(aes(fill=pct))
)
})
```
### Bar
```{r graph_2, fig.height=4, fig.width=3}
renderPlot({
ggplot(data=state_dat) +
geom_col(aes(state,pct,fill=pct))
})
```

ggplot, drawing lines across multiple facets (x axis is date)

My question is almost identical to this question EXCEPT that I am using dates for my x axis. I have tried the code from the answer in the linked question. The example provided works for me, but I cannot get it to work for my dataset. I am guessing it is because of the dates?
(Sorry I could not comment on the previous question chain - I'm new and don't have enough points to comment)
Here is the sample code:
library(ggplot2)
library(gtable)
library(grid)
data<-data.frame(Date=rep(seq(as.Date("2018-09-22","%Y-%m-%d"),
as.Date("2019-06-19","%Y-%m-%d"),
by=30),9),
Station=c(rep("A",30),rep("B",30),rep("C",30)),
Description=rep(c(rep("Var1",10),rep("Var2",10),
rep("Var3",10)),3),
Data=c(seq(1,10,by=1),seq(500,800,length.out=10),seq(30,90,length.out=10), seq(5,19,length.out=10),seq(450,1080,length.out=10),seq(20,60,length.out=10), seq(2,15,length.out=10),seq(600,750,length.out=10),seq(80,25,length.out=10)))
plot<-ggplot(data,aes(x=Date,y=Data,color=as.factor(Station)))+
geom_line(size=1)+
facet_grid(Description~.,scales="free_y",switch="y")+
xlab("")+
ylab("")+
theme(panel.background=element_blank(),
panel.grid.major.y=element_line(color="grey80",
size=0.25),
panel.grid.major.x=element_blank(),
axis.line=element_line(color="black"),
strip.placement="outside",
strip.background=element_blank(),
legend.position="top",
legend.key=element_blank(),
legend.title=element_blank())
plot
plot.b<-ggplot_build(plot)
plot.g<-ggplot_gtable(plot.b)
data2npc <- function(x, panel = 1L, axis = "x") {
range <- plot.b$layout$panel_params[[panel]][[paste0(axis,".range")]]
scales::rescale(c(range, x), c(0,1))[-c(1,2)]
}
start <- sapply(as.Date("2018-10-10"),"%Y-%m-%d"), data2npc, panel=1, axis="x")
plot.g <- gtable_add_grob(plot.g, segmentsGrob(x0=start, x1=start, y0=0, y1=1, gp=gpar(lty=2)), t=7, b=9,l=5)
grid.newpage()
grid.draw(plot.g)
resulting plot
I have figured out my own answer!
The key is in changing t, b, and l in gtable_add_grob:
plot.g <- gtable_add_grob(plot.g, segmentsGrob(x0=start, x1=start, y0=0, y1=1, gp=gpar(lty=2)), t=7, b=13,l=7)
Although, it seems to me to be trial and error to identify the correct values of t,b, and l.
New code:
library(ggplot2)
library(gtable)
library(grid)
data<-data.frame(Date=rep(seq(as.Date("2018-09-22","%Y-%m-%d"),
as.Date("2019-06-19","%Y-%m-%d"),
by=30),9),
Station=c(rep("A",30),rep("B",30),rep("C",30)),
Description=rep(c(rep("Var1",10),rep("Var2",10),
rep("Var3",10)),3),
Data=c(seq(1,10,by=1),seq(500,800,length.out=10),seq(30,90,length.out=10), seq(5,19,length.out=10),seq(450,1080,length.out=10),seq(20,60,length.out=10), seq(2,15,length.out=10),seq(600,750,length.out=10),seq(80,25,length.out=10)))
plot<-ggplot(data,aes(x=Date,y=Data,color=as.factor(Station)))+
geom_line(size=1)+
facet_grid(Description~.,scales="free_y",switch="y")+
xlab("")+
ylab("")+
theme(panel.background=element_blank(),
panel.grid.major.y=element_line(color="grey80",
size=0.25),
panel.grid.major.x=element_blank(),
axis.line=element_line(color="black"),
strip.placement="outside",
strip.background=element_blank(),
legend.position="top",
legend.key=element_blank(),
legend.title=element_blank())
plot
plot.b<-ggplot_build(plot)
plot.g<-ggplot_gtable(plot.b)
data2npc <- function(x, panel = 1L, axis = "x") {
range <- plot.b$layout$panel_params[[panel]][[paste0(axis,".range")]]
scales::rescale(c(range, x), c(0,1))[-c(1,2)]
}
start <- sapply(as.Date("2018-10-10","%Y-%m-%d"), data2npc, panel=1, axis="x")
plot.g <- gtable_add_grob(plot.g, segmentsGrob(x0=start, x1=start, y0=0, y1=1, gp=gpar(lty=2)), t=7, b=13,l=7)
grid.newpage()
grid.draw(plot.g)
And new resulting plot

ggplot plotly API mess width stack bar graph

I am using plotly library to get me HTML interactive graph, which i already generating from ggplot2, but with stacked graph, plotly doesnt work properly.
Here is my ggplot code :
if(file.exists(filename)) {
data = read.table(filename,sep=",",header=T)
} else {
g <- paste0("=== [E] Error : Couldn't Found File : ",filename)
print (g)
}
ReadChData <- data[data$Channel %in% c("R"),]
#head(ReadChData,10)
# calculate midpoints of bars (simplified using comment by #DWin)
Data <- ddply(ReadChData, .(qos_level),
transform, pos = cumsum(AvgBandwidth) - (0.5 *AvgBandwidth)
)
# library(dplyr) ## If using dplyr...
# Data <- group_by(Data,Year) %>%
# mutate(pos = cumsum(Frequency) - (0.5 * Frequency))
# plot bars and add text
g <- ggplot(Data, aes(x = qos_level, y = AvgBandwidth)) +
scale_x_continuous(breaks = x_axis_break) +
geom_bar(aes(fill = MasterID), stat="identity", width=0.2) +
scale_colour_gradientn(colours = rainbow(7)) +
geom_text(aes(label = AvgBandwidth, y = pos), size = 3) +
theme_set(theme_bw()) +
ylab("Bandwidth (GB/s)") +
xlab("QoS Level") +
ggtitle("Qos Compting Stream")
png(paste0(opt$out,"/",GraphName,".png"),width=6*ppi, height=6*ppi, res=ppi)
print (g)
library(plotly)
p <- ggplotly(g)
#libdir arugumet will be use to point to commin lib
htmlwidgets::saveWidget(as.widget(p), selfcontained=FALSE, paste0(opt$out,"/qos_competing_stream.html"))
and here is HTML output form plotly lib
http://pasteboard.co/2fHQfJwFu.jpg
Please help.
This is perhaps quite a bit late to answer. But for someone who might have the issue in future...
The geom_bar's width parameter is not recognized by ggplotly function.
Work Around :
A work around (not very good one) by using parameters colour="white", size = 1. This basically adds a white line around the bars, making an effect like white space.
You could try the following:
stat_summary(aes(fill = MasterID), geom="bar", colour="white", size = 1, fun.y = "sum", position = "stack")
Better solution :
Use bargap parameter from layout function. The code should be:
ggplotly(type='bar', ...) %>% layout(bargap = 3, autosize=T)
P.S. the code in question code is not executable, throws an error due to missing filename.