Plotly & Shiny, reactive height of plots

Hi,

I’m trying to integrate plotly into an existing shiny app by using the associated functions ggplotly, renderPlotly, and plotlyOutput. In the original script, the height of the box containing the plot was set conditionally, based on how many elements were being plotted. To keep the graphs from becoming too small, I had:

facet_wrap(~symbol, ncol = 2)
plotOutput(“plotTSNE_Markers”, inline = T)
and
output$plotTSNE <- renderPlot({
#mycode… function to plot new graph
})
}, width = 700,
height = function(){
dat_length <- length(subset(annotations, GeneName%in%data(), GeneID, drop=T))
if (dat_length <= 4) {
return(650)
} else if (dat_length > 4) {
return(650 + 325*ceiling((dat_length - 4)/2))
}
}).

The height function allowed for the plots to automatically extend downward on the webpage if there were more than 4 graphs being plotted, i.e. creating a minimum graph size and extending accordingly if there are over 4 graphs.

It seems that with plotlyOutput, there is no parameter for inline = T, and there is no support for a height parameter in plotlyOutput. I was wondering if there is a workaround to prevent the graphs from becoming too squished together, especially when there are more than 4.

The current behavior from plotly is below, I would like to have a layout with 2 columns, that extends downwards on the webpage when there are >4 graphs being plotted:

P.S. What is causing my y axis label to be cut off? My code is plotlyOutput(“plotTSNE_Markers”, height = “auto”, width = “auto”)

Thanks!
Justin

1 Like

From the function definition: plotlyOutput <- function(outputId, width = "100%", height = "400px"),
it appears that plotlyOutput accepts a height parameter which you should be able to set.
You can try to manually set the width of the plotlyOutput and see if that allows for the y axis to show up better.

Hey Kevin,

Currently I have height and width parameters both set to “auto”

box(plotlyOutput(“plotTSNE”, width = “auto”, height = “auto”),
height = NULL,
width = NULL,
status = “warning”
)

I have played around with the width settings and it’s still cut off… and height/width seem like they only accept formats such as “500px”, “auto”, or “100%”.

If you have Shiny, you can see that the plotOutput() function has a parameter called “inline,” which allows for the height/width to be set reactively via a function in the server <- function(input, output, session) {} part of the app. So if there are multiple elements to be plotted, reactivity can change the UI formatting accordingly. With plotlyOutput, it doesn’t have an inline parameter, so it seems like the height/width can only be fixed from within the script- I’m wondering if there are options to mimic this behavior.

Perhaps you can try this:

htmltools::div(style = "display:inline-block", plotlyOutput("y", width = 250, height = 400))
which should serve the same purpose as plotlyOutput("y", width = 250, height = 400, inline = T)

Apologies for late response,

I just attempted to implement the code that you mentioned, but wasn’t able to get it to work.

Can you please explain your approach?

I think in order to solve my issue it needs to make use of reactivity somehow, because it all depends on how many inputs the user specifies…

Bumping for visibility

see here for some additional discussion https://github.com/ropensci/plotly/issues/510

It seems however there are two issues:

  1. yaxis being cutoff
  2. dynamic resizing and adding of htmwidgets in a Shiny app

yaxis cut off

The yaxis problem might be solved by using the margin parameter with layout.

plot_ly() %>% layout(margin = list(l=100))

dynamic resizing and adding of htmlwidgets

This Github issue offers the best discussion on different ways to achieve this. Here is a very minimal example to accomplish what I think you would like. To work around the axis issues with ggplotly and facets, you might need to explore subplots with plot_ly. Let me know if you would like any help with that.

library(shiny)
library(plotly)

# make a chart that we will use throughout
#  this is just for convenience in this example
p <- ggplotly(
  ggplot(mtcars, aes(x=mpg,y=hp,color=factor(cyl))) +
    geom_point() +
    facet_wrap(~cyl, ncol=1)
) %>%
  layout(margin=list(r=100, l=70, t=20, b=70))

# for better layout
#   fluidPage and flexdashboard offers lots of helpers
#   but let's see how we can do it in old-school html/css
ui <- tagList(
  numericInput("nplot","Number of plots",2),
  uiOutput(
    'chartcontainer'
  )
)

server <- function(input, output, session) {
  output$chartcontainer <- renderUI({
    tagList(
      lapply(
        seq_len(input$nplot),
        function(x){
          htmltools::tags$div(
            style="display:block;float:left;width:45%;height:50%;",
            tags$h3(paste0("plot #",x)),
            #NOTE: inside of renderUI, need to wrap plotly chart with as.tags
            htmltools::as.tags(p)
          )
        }
      )
    )
  })
}

shinyApp(ui,server)

Thanks for the great response. The y-axis label issue has been resolved using the advice on layout and margins…

Regarding dynamic resizing, would the above strategy essentially work like Shiny’s plotOutput(inline = T) parameter? From my screenshot, you can see that getting 2 columns is no problem with facets… just that the graphs become too squished to see any detail. I am just trying to get the webpage to extend downward automatically to keep the plots a reasonable size.

I found that this inline = T feature has been discussed before but it doesn’t seem like it has been added yet: https://github.com/ropensci/plotly/pull/385/commits

I’ll take a look at your link and code, thanks.

If I understand correctly, you could change the style in my example to something like

style="display:inline;width:100%;height:80%;"

The inline feature in effect does something very similar.

@timelyportfolio, your solution is very useful. Many thanks!
If there are many plots, is it possible to paginate by supplying nrow and ncol like parameters that would determine how many plots would fit in each page?