How do I use purrr::map() to create function with plotly subplot() without getting an error?

Viewed 338

I'm looking to use purrr::map() to create a subplot function in plotly. This previous question R: Plotly and subplot(): fastest way to create a subplot based on a factor provided map as a solution, but it looks like this doesn't work anymore with more recent versions of purrr. When I try and run the code below I get Error in : Can't convert a plotly/htmlwidget object to function.

In the example below I want 4 charts on 2 rows, with hover info, a shared legend all as a stacked 100% bar chart. Can purrr::map() work with this in plotly? Thanks in advance for your help.

library(purrr)
library(plotly)
library(dplyr)
library(tibble)

#basic dataframe

df <- 
  tibble::tribble(~cakes,~year,~not_sold,~sold,
                  "Cheese Cake",    2018,   21757,  6,
                  "Cheese Cake",    2019,   18115,  200,
                  "Cheese Cake",    2020,   16776,  2920,
                  "Carrot Cake",    2018,   2529,   2,
                  "Carrot Cake",    2019,   2142,   351,
                  "Carrot Cake",    2020,   1662,   298,
                  "Pound Cake", 2018,   7004,   21,
                  "Pound Cake", 2019,   8739,   316,
                  "Pound Cake", 2020,   6832,   2949,
                  "Chocolate Cake", 2018,   23761,  35,
                  "Chocolate Cake", 2019,   18973,  1379,
                  "Chocolate Cake", 2020,   14054,  6065)

#adding percentage columns

df <- 
  df %>% 
  dplyr::mutate(total = sold + not_sold,
                pct_sold = (sold/total)*100,
                pct_not = (not_sold/total)*100)
#subplotting

cakes <- 
  df %>% 
    split(.$cakes) %>% 
    purrr::map(.x = df,
               .f = 
        plotly::plot_ly(
          x = ~year, 
          y = ~pct_sold, 
          name = "Cakes sold",
          type = 'bar',
          text = paste0('Cakes sold: ',
                         format(~sold,
                                big.mark = ",")),
          hoverinfo = 'text',
          marker = list(color = 'rgb(0, 169, 244)',
                        width = 2))) %>%
          plotly::add_trace(
            y = ~pct_not,
            name = "Not sold",
            text = paste0('Cakes not sold: ',
                          format(~not_sold,
                                 big.mark = ",")),
            hoverinfo = 'text',
            marker = list(color = 'rgb(230, 230, 230)'),
                          width = 2)) %>%
          plotly::layout(barmode = 'stack') %>%
  plotly::subplot(nrows = 2,
                  margin = .05)

cakes
1 Answers

Another option is to use the new dplyr 1.0.0 features to nest your data and create a a dataframe column containing the plots you need.

plot_fn <- function(data) {
  plotly::plot_ly(
    data,
    x = ~year, 
    y = ~pct_sold, 
    name = "Cakes sold",
    type = 'bar',
    text = paste0('Cakes sold: ',
                  format(~sold,
                         big.mark = ",")),
    hoverinfo = 'text',
    marker = list(color = 'rgb(0, 169, 244)',
                  width = 2)) %>%
  plotly::add_trace(
    y = ~pct_not,
    name = "Not sold",
    text = paste0('Cakes not sold: ',
                  format(~not_sold,
                         big.mark = ",")),
    hoverinfo = 'text',
    marker = list(color = 'rgb(230, 230, 230)'),
    width = 2)
}

df_plots <- df %>% 
  dplyr::nest_by(cakes) %>% 
  dplyr::mutate(plot = list(plot_fn(data)))

df_plots

This creates a dataframe with your plotly objects inside it.

# A tibble: 4 x 3
# Rowwise:  cakes
  cakes                        data plot    
  <chr>          <list<tbl_df[,6]>> <list>  
1 Carrot Cake               [3 × 6] <plotly>
2 Cheese Cake               [3 × 6] <plotly>
3 Chocolate Cake            [3 × 6] <plotly>
4 Pound Cake                [3 × 6] <plotly>

You can then pipe further to generate subplots etc.

Related