Plotly 'Download as png' resizing image when used in RShiny application - r

I'm using plotly package for R to display some large graphs in my shiny application, the graphs display nicely as intended. However, when I click the "Download as png" button, the downloaded .png has been resized. here is a demo of this behavior.
How can I specify the resolution for the exported plot?
Here is a minimal example that demonstates the issue by having a really long title that gets clipped on download
app.R
library(shiny)
library(ggplot2)
library(plotly)
ui <- fluidPage(titlePanel("Plotly Download demo"),
plotlyOutput("demoPlotly"))
server <- function(input, output) {
output$demoPlotly <- renderPlotly({
#make an arbritrary graph with a long title
p <- iris %>%
ggplot(aes(x = Petal.Length, y = Petal.Width, color = Species)) +
geom_point() +
labs(title = "This is a really long title to demo my problem of plotly resizing my downloaded output")
ggplotly(p)
})
}
# Run the application
shinyApp(ui = ui, server = server)
The downloaded graph looks like this:

Michael,
Take a look at plotly_IMAGE(). [Version 4.7.1]. The function does allow you to specify width and height for the saved image. For example 800 by 600.
Regarding your example, I have provided an example of how you might modify the layout to accommodate the title wrapping. Note the changes the margin and padding. I also inserted a call to plotly_IMAGE to provide a simplified example of its usage - it slows things down as it is in line with the plot generation. I'd recommend you add a button and separate out from image display.
Hope this helps
Take care
T.
Pre-requisite - Plotly Credentials:
To use this plotly example you need to register and obtain a plotly api key. Once obtained you need add these entries to your ~/.Renviron file.
Ref: https://plot.ly/r/getting-started/
Example .Renviron file entries
plotly_username="xxxxx"
plotly_api_key="xxxxxx"
Example Code
library(shiny)
library(ggplot2)
library(plotly)
wrapper <- function(x, ...) {
paste(strwrap(x, ...), collapse = "\n")
}
# Example usage wrapper(my_title, width = 20)
my_title <- "This is a really long title to demo my problem of plotly resizing my downloaded output"
ui <- fluidPage(titlePanel("Plotly Download demo"), plotlyOutput("demoPlotly"))
pal <- c("blue", "red", "green")
pal <- setNames(pal, c("virginica", "setosa", "versicolor"))
layout <- list(title = wrapper(my_title, width = 60),
xaxis = list(title = "Petal Length"),
yaxis = list(title = "Petal Width"),
margin = list(l = 50, r = 50, b = 100, t = 100, pad = 4))
server <- function(input, output) {
output$demoPlotly <- renderPlotly({
# make an arbitrary graph with a long title
p <- iris %>%
plot_ly(x = ~Sepal.Length,
y = ~Petal.Width,
color = ~Species,
colors = pal,
mode = "markers",
type = "scatter") %>%
layout(autosize = F,
title = layout$title,
xaxis = layout$xaxis,
yaxis = layout$yaxis,
margin = layout$margin)
p$elementId <- NULL
# Example usage of plotly image
plotly_IMAGE(p,
width = 800,
height = 600,
format = "png",
scale = 1,
out_file = "output.png")
p
})
}
# Run the application
shinyApp(ui = ui, server = server)
Screen Shot Image - screen capture
Plotly save - screen click
plotly_IMAGE() save image output 800 by 600

Related

Shiny plotly : how to fix hidden hovermode tooltip at the top of the graph

I have a shiny app with a Plotly in the middle of the screen with multiple lines. I am using "x unified" hovermode, so when I put the cursor on the plot, I can see the value for each line. However, when I have a lot of lines, the tooltip of "hovermode" is cut at the top of the graph, and I am trying to make it entirely visible.
What I already tried and doesn't work:
put a higher z-index on the graph ph
add margin or padding to the graph, but the tooltip is still cut at the top of the graph
Any idea how I can fix this ?
library(shiny)
library(plotly)
df <- data.frame(
xvar = rep(1:5, each = 26),
yvar = sample(1:100, 5*26, replace = TRUE),
groupvar = rep(letters, 5)
)
ui <- fluidPage(
tags$div(id = "my_div", style = "height:100px; background-color: blue; z-index: 0"),
plotlyOutput("my_plot") # I need the plot hovermode to be over the previous div
)
server <- function(input, output) {
output$my_plot <- renderPlotly({
fig <- plot_ly(df, x = ~xvar, y = ~yvar, type = 'scatter', mode = 'lines',
name = ~groupvar, color = ~groupvar)
fig <- fig %>% layout(hovermode = "x unified")
fig
})
}
shinyApp(ui = ui, server = server)
If it doesn't affect the layout of your shiny app, increasing the height of the plot allows the hover label to stay within the borders and prevents overflow.
ui <- fluidPage(
tags$div(id = "my_div", style = "height:100px; background-color: blue; z-index: 0"),
plotlyOutput("my_plot", height = "600px")
)

Changing Dimensions of downloaded plot of plotly in r shiny

I have a code which downloads the png image of the graph formed in plotly with same dimensions everytime. I want to generate a image with higher quality in it. Like if i click on the buttons provided at the top right corner in plotly graphs, it should download with different dimensions
Reference code is given below
library(shiny)
library(plotly)
ui <- fluidPage(
selectInput("choice", "Choose", choices = names(iris), selected = NULL),
plotlyOutput("graph")
)
server <- function(input, output, session){
output$graph <- renderPlotly({
plot_ly(iris, x = ~get(input$choice), y = ~Sepal.Length, type = 'scatter', mode = 'markers')
})
}
shinyApp(ui, server)
You can use toImageButtonOptions in the config function to set the dimensions:
plot_ly(
iris, x = ~get(input$choice), y = ~Sepal.Length, type = 'scatter', mode = 'markers'
) %>% config(
toImageButtonOptions = list(format = "png", width = 1500, height = 750)
)

Can I programmatically control plotly zoom functionality without having to re-plot in an R shiny app?

I am trying to make a shiny app in R using Plotly plots.
I am trying to create zoom functionality in the backend of a shiny app, responsive to a click event on a plotly graph (i.e. when a point is clicked, zoom in on that point). However, the only solution I have found so far is to completely re-layout the plotly object with new view ranges.
For large plots, this is incredibly slow because shiny re-renders the entire plot, and is much slower than plotly's built-in zoom functionality on the front end (the plotly user interface buttons at the top right) when the number of data points is large. Is there a way to use the plotly zoom functionality in the backend so that the whole plotly object doesn't have to re-render to zoom?
Example:
library(plotly)
library(shiny)
ui <- fluidPage(
plotlyOutput("scatter")
)
server <- function(input, output) {
zoom_vals = reactiveValues(xrange=NA,yrange=NA)
# Plot scatter plot
output$scatter <- renderPlotly({
data <- data.frame(x=sample.int(1000,100), y = sample.int(1000,100))
x_axis = list(range = zoom_vals$xrange)
y_axis = list(range = zoom_vals$yrange)
plot_ly(data, x = ~x, y = ~y) %>% layout(xaxis=x_axis, yaxis=y_axis)
})
# Catch plot click
observeEvent(event_data("plotly_click"),{
d<-event_data("plotly_click")
zoom_vals$xrange <- c((d$x- 1),(d$x+ 1))
zoom_vals$yrange <- c((d$y- 1),(d$y- 1))
})
}
shinyApp(ui, server)
You can modify (no re-rendering) an exisiting plotly object in shiny via plotlyProxyInvoke.
To change the axis range we'll need the relayout method:
library(plotly)
library(shiny)
ui <- fluidPage(plotlyOutput("scatter"))
server <- function(input, output) {
# Plot scatter plot
output$scatter <- renderPlotly({
data <- data.frame(x = sample.int(1000, 100), y = sample.int(1000, 100))
plot_ly(data, x = ~ x, y = ~ y, type = "scatter", mode = "markers")
})
scatterProxy <- plotlyProxy("scatter")
# Catch plot click
observeEvent(event_data("plotly_click"), {
d <- event_data("plotly_click")
xrange <- c((d$x - 100), (d$x + 100))
yrange <- c((d$y - 100), (d$y + 100))
plotlyProxyInvoke(scatterProxy, "relayout", list(xaxis = list(range = xrange), yaxis = list(range = yrange)))
})
}
shinyApp(ui, server)

geom_label doesn't scale correspondingly to plot in Shiny

While building up my Shiny App I came across situation where ggplot2 graphic looks very different with different window sizes. The first graphic shows the plot in full desktop size - everything works great:
However, when I change the size of output window every element seems to scale down properly, but not geom_label (see the graphic below).
Why is that a case and how can I make the geom_label to scale down correspondigly?
The Shiny set-up is:
ui <- fluidPage(
mainPanel(
selectInput('cluster', '', 1:7),
plotOutput('ap_plot', height = 200)
)
)
)
server <- function(input, output, session) {
output$ap_plot <- renderPlot({
data %>%
filter(cluster == input$cluster) %>%
plot_sequences(.by = ts_cluster, .colour = id)
})
}
shinyApp(ui = ui, server = server)
As detailed in the shiny documentation the plot width for the current plot is defined within session$client_data$output_<plotname>_width, so for your example session$client_data$output_ap_plot_width. This can be used to scale the text argument of geom_label. You don't provide a minimal reproducible example, but here's one:
data <- tibble(
cluster = sample(7, 100, replace = TRUE),
x = rnorm(100),
y = rnorm(100)
)
plot_sequences <- function(data_set, width) {
label_data <- data_set %>%
summarise(
n = n(),
mean_x = mean(x),
mean_y = mean(y),
label = sprintf("N: %d\nMean x: %0.3f\nMean y: %0.3f", n, n, mean_x, mean_y)
)
ggplot(data, aes(x, y)) +
geom_point() +
geom_label(aes(x = 1.5, y = 1.5, label = label), label_data, size = 4 / 900 * width)
}
ui <- fluidPage(
mainPanel(
width = 12,
selectInput('cluster', '', 1:7),
plotOutput('ap_plot', height = 200)
)
)
server <- function(input, output, session) {
width <- 400
output$ap_plot <- renderPlot(execOnResize = TRUE, {
data %>%
filter(cluster == input$cluster) %>%
plot_sequences(session$clientData[["output_ap_plot_width"]])
})
}
shinyApp(ui = ui, server = server)
You can see that my plot function takes the plot width as an input and then scales the text size accordingly, using a width of 900 pixels as a baseline. Note also that I've set execOnResize to be TRUE, since otherwise the plot is replayed rather than recalculated when the window/plot are resized.

Reactive resizing of plotly graph in R Shiny

My problem here is, I think, an edge case for this type of problem. With that said, I've got some reproducible code here that might help a lot of people with similar issues they have, particularly the issue of changing a ggplot's plot height in RShiny (which is default set to 400px).
Here is a demo of a shiny app where the height of the ggplot is set equal to the width of the ggplot:
mydf = data.frame(x = 1:5, y = 1:5)
# User Interface
# =================
ui <- fluidPage(theme = shinytheme('united'),
fluidRow(
column(width = 12, align = 'center',
plotOutput('myplot', height = 'auto')
)
))
# ====
# Server Code
# ==============
server <- shinyServer(function(input, output, session) {
# 3. Create the chart
# ===-===-===-===-===-===
output$myplot <- renderPlot({
ggplot(mydf, aes(x = x, y = x)) + geom_point()
}, height = function() { session$clientData$output_myplot_width })
})
# ====
shinyApp(ui, server)
Play around with the width of the window launched by launching this shiny app. By setting height = 'auto' in the UI, and then setting height = function() { session$clientData$output_myplot_width } in the Server, the height is dynamically updated. Very good indeed.
I am working on something similar in plotly, however a significant constraint that I have is that I currently need to pass a parameter for height and width to my call of plotly(), due to the very specific way I am trying to display the markers on my plot. An example of what I am trying to do is as such:
mydf <- data.frame(x = 1:5, y = 1:5)
myplotlyfunction <- function(mydf, myplotheight) {
plot_ly(mydf, height = myplotheight, width = myplotheight * 1.2) %>%
add_trace(x = ~x, y = ~y, type = 'scatter', mode = 'markers')
}
# User Interface
# =================
ui <- fluidPage(theme = shinytheme('united'),
fluidRow(
column(width = 12, align = 'center',
plotlyOutput('myplotly')
)
))
# ====
# Server Code
# ==============
server <- shinyServer(function(input, output, session) {
# 3. Create the chart
# ===-===-===-===-===-===
output$myplotly <- renderPlotly({
this.height <- 600
myplotlyfunction(mydf, myplotheight = this.height)
})
})
# ====
shinyApp(ui, server)
The specific reason why I need to pass a height and width parameter to the plot_ly() call is deserving of a post of its own, and unfortunately I haven't thought of a work around yet. Here is a real example of my plot_ly output -
At a high level, the marker size parameter for plot_ly scatter plots sets the marker sizes based on pixels, and my plot needs very specifically sized markers, and these marker sizes are therefore a function of the plot size.
Here is my attempt to make my plot_ly plots dynamic in Shiny, despite having to initially pass a fixed parameter to the plot_ly height. The idea is motivated on the initial ggplot() dynamic height function:
mydf <- data.frame(x = 1:5, y = 1:5)
myplotlyfunction <- function(mydf, myplotheight) {
plot_ly(mydf, height = myplotheight, width = myplotheight * 1.2) %>%
add_trace(x = ~x, y = ~y, type = 'scatter', mode = 'markers')
}
# User Interface
# =================
ui <- fluidPage(theme = shinytheme('united'),
fluidRow(
column(width = 12, align = 'center',
plotlyOutput('myplotly', height = 'auto')
)
))
# ====
# Server Code
# ==============
server <- shinyServer(function(input, output, session) {
# 3. Create the chart
# ===-===-===-===-===-===
output$myplotly <- renderPlotly({
this.height <- function() { session$clientData$output_myplotly_width }
myplotlyfunction(mydf, myplotheight = this.height)
}, height = function() { session$clientData$output_myplotly_width })
})
# ====
shinyApp(ui, server)
This does not work, and in fact the app crashes right away.
Thank you a ton for anybody who made it to the end of this post. Any help is appreciated regarding how I can dynamically resize a plot_ly() plot in R Shiny, based on the height or width of the app window, given that the plot_ly function itself requires a height parameter. Thoughts on how to solve the underlying problem of needing a height parameter to plot_ly() in the first place are appreciated as well, but that's a separate issue (that I've posted about myself, here - Set marker size based on coordinate values, not pixels, in plotly R - and in other posts as well).
Once again, thanks a ton for any help!

Resources