With dplyr 1.0.0+ we can start to treat objects like data in the tidyverse. Here’s an example.

library(dplyr)
library(plotly)

Let’s create a simple dataset about cakes.

cakes <- 
  tibble::tribble(~cake,~year,~sold,~not_sold,
                  "Chocolate",    2018,   21757,  6,
                  "Chocolate",    2019,   18115,  200,
                  "Chocolate",    2020,   16776,  2920,
                  "Lemon",    2018,   2529,   2,
                  "Lemon",    2019,   2142,   351,
                  "Lemon",    2020,   1662,   298,
                  "Carrot", 2018,   7004,   21,
                  "Carrot", 2019,   8739,   316,
                  "Carrot", 2020,   6832,   2949,
                  "Coffee",   2018,   23761,  35,
                  "Coffee",   2019,   18973,  1379,
                  "Coffee",   2020,   14054,  6065,
                  "Victoria Sponge",    2018,   11645,  128,
                  "Victoria Sponge",    2019,   10932,  2348,
                  "Victoria Sponge",    2020,   6964,   6323,
                  "Black Forest",    2018,   30739,  12,
                  "Black Forest",    2019,   38033,  697,
                  "Black Forest",    2020,   17173,  4337)

Now let’s add some extra columns to the dataset and let’s also write a function to create a pretty plotly bar chart:

#adding columns

cakes <- 
  cakes %>% 
  dplyr::mutate(cake_name = cake,
                year = as.factor(year),
                total = sold + not_sold,
                pct_sold = (sold/total)*100,
                pct_not = (not_sold/total)*100)

# function to generate pretty bar chart with plotly

plot_fn <- function(df) {
  plotly::plot_ly(
    data = df, 
    x = ~year, 
    y = ~pct_sold, 
    legendgroup = ~pct_sold,
    showlegend = FALSE,
    name = "Cakes sold",
    type = 'bar',
    text = paste0('Cakes sold: ',
                  format(df$sold,
                         big.mark = ",")),
    hoverinfo = 'text',
    marker = list(color = 'rgba(0, 169, 244, 1)',
                  line = list(color = 'rgb(0, 0, 0)'),
                  width = 2)) %>%
    plotly::add_trace(
      y = ~pct_not, 
      name = "Cakes Not Sold",
      legendgroup = ~pct_not,
      showlegend = FALSE,
      text = paste0('Cakes not sold: ',
                    format(df$not_sold,
                           big.mark = ",")),
      hoverinfo = 'text',
      marker = list(color = 'rgba(230, 230, 230, 1)',
                    line = list(color = 'rgb(0, 0, 0)'),
                    width = 2)) %>%
    plotly::layout(
      xaxis = list(tickangle = 0,
                   showgrid = FALSE,
                   showline = TRUE,
                   showticklabels = TRUE,
                   title = "",
                   zeroline = TRUE),
      yaxis = list(title = "",
                   showgrid = FALSE,
                   showline = FALSE,
                   showticklabels = FALSE,
                   zeroline = FALSE),
      annotations = list(x = 0.00,
                         y = 1.1,
                         xanchor = 'left',
                         yanchor = 'top',
                         text = paste0("<b>", unique(df$cake_name), "</b>"),
                         showarrow = F,
                         xref='paper',
                         yref='paper'),
      barmode = 'stack',
      showlegend = FALSE) %>%
    plotly::add_annotations(yref = 'y',
                            y = ~pct_sold / 2,
                            text = paste0(round(df$pct_sold,
                                                0), 
                                          '%'),
                            font = list(family = 'Arial',
                                        size = 12,
                                        color = 'rgb(248, 248, 255)'),
                            showarrow = FALSE) %>%
    plotly::add_annotations(y = ~pct_sold + pct_not,
                            text = format(df$total, 
                                          big.mark=","),
                            yanchor = 'bottom',
                            showarrow = FALSE)
}

Here’s what a bar chart would look like for, say, Chocolate cake:

cakes %>% 
  dplyr::filter(cake == "Chocolate") %>% 
  plot_fn()
## Warning: `arrange_()` is deprecated as of dplyr 0.7.0.
## Please use `arrange()` instead.
## See vignette('programming') for more help
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.

What if we wanted to generate this for every cake? We can nest our data and collect all the different plots in a dataframe:

cakes %>% 
  dplyr::nest_by(cake) %>% 
  dplyr::transmute(plot = list(plot_fn(data)))
## # A tibble: 6 x 2
## # Rowwise:  cake
##   cake            plot    
##   <chr>           <list>  
## 1 Black Forest    <plotly>
## 2 Carrot          <plotly>
## 3 Chocolate       <plotly>
## 4 Coffee          <plotly>
## 5 Lemon           <plotly>
## 6 Victoria Sponge <plotly>

This allows us to easily create pretty subplots by piping straight from the original dataset:

cakes %>% 
  dplyr::nest_by(cake) %>% 
  dplyr::transmute(plot = list(plot_fn(data))) %>% 
  dplyr::select(plot) %>% 
  plotly::subplot(nrows = 2, margin = 0.05)