Search code examples
rggplot2shinyflexdashboardpatchwork

Dynamic number of plots with flexdashboard, lapply and patchwork


I have a variable list of plots that does not depend on the number of plots a user selects. Rather, they input data, and given their data structure, I return the appropriate number of plots in a list. The user is unaware of the appropriate number of plots - my code figures it out for them. As such, the number of objects in the list is not an input that they select.

I've successfully used lapply to generate multiple plots in RStudio, but only the last plot appears in my Shiny App. I think I've seen this discussed on some traditional shiny apps, but I'm having trouble translating it to flexdashboard/RMD.

I've included a simple reprex below. The number of objects in iris_list is variable. I use lapply to bind together two plots from each object/dataset in the list, and patchwork to add a shared legend (not included in the reprex). How can I get all object in the list to appear in my app? I get the impression that I need multiple renderPlots and an Observe statement.

---
title: "my app"
runtime: shiny
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    source_code: embed
    theme: flatly
always_allow_html: yes
---

### Plots

```{r chunk}

renderPlot({

  iris_list<- list(iris,iris)
  
  p1 <- map(iris_list,~ggplot(data=.x,aes(x=Sepal.Length))+geom_histogram())
  p2 <- map(iris_list,~ggplot(data=.x,aes(x=Petal.Length))+geom_histogram())
  
  plots_combined <- lapply(seq(length(iris_list)),function(x) c(p1[x],p2[x]))
  
  lapply(seq(length(plots_combined)), function(x) wrap_plots(plots_combined[[x]])+
           plot_annotation(title=paste("plot",x)))

})
```

Solution

  • The answer can be found here: R Markdown Shiny renderPlot list of plots from lapply. I misread it earlier, but the answer was right in front of me the whole time.

    The trick is to change the renderPlot to reactive, put it into an object, and then add the renderUI and the observe statements. This works perfectly in flexdashboard. The width and height of the plot go after the renderPlot statement as they normally would, e.g.

    renderPlot({
                
            }, width=900,height = 325)