Search code examples
rggplot2plotlyflexdashboardggplotly

Automatically creating subplots in plotly (e.g. Facetting in R)


I'm quite comfortable using the tidyverse and ggplot. I'm trying to produce an interactive graph to deploy with flexdashboard. I'm therefore trying to generate my usual ggplots in plotly.

Suppose I have the following dataframe:

data.frame(id = c(1:5),
           product = c("product1","product2","product1","product3","product2"),
           variable = c("var1","var1","var3","var2","var1"),
           price = c(100,120,140,90,80))

With output:

  id  product variable price
1  1 product1     var1   100
2  2 product2     var1   120
3  3 product1     var3   140
4  4 product3     var2    90
5  5 product2     var1    80

If I want to display all of this on a plot, I would do the following in ggplot:

library(tidyverse)
library(hrbrthemes)

data.frame(id = c(1:5),
           product = c("product1","product2","product1","product3","product2"),
           variable = c("var1","var1","var3","var2","var1"),
           price = c(100,120,140,90,80)) %>%
  ggplot(aes(x = id, y = price, color = variable)) +
  geom_point() +
  facet_wrap(~product) +
  theme_ft_rc()

Which woud yield:

Sample Plot

I understand I can achieve something similar in plotly by using the subplot() function. The issues is that I have somewhere between 14-28 categories to plot as facets. Which as far as I understand, would mean that I would have to produce 14-28 plots and then arrange them in a grid. It seems a bit tedious and I was wondering if there was a more efficient way to achieve that, such as the facet option in ggplot. I also got a snippet of code on another post:

library(plotly)

dataframe <- data.frame(id = c(1:5),
           product = c("product1","product2","product1","product3","product2"),
           variable = c("var1","var1","var3","var2","var1"),
           price = c(100,120,140,90,80)) %>%
  pivot_wider(names_from = "product", values_from = "price")

vars <- setdiff(names(dataframe),"id")

plots <- lapply(vars, function(var){
   plot_ly(dataframe, x = ~id, color =~variable, y = as.formula(paste0("~",var))) %>%
  add_bars(name = var)
 })
 subplot(plots, nrows = length(plots), shareX = TRUE, titleX = FALSE)

Which yields:

enter image description here

and requires to use tidyr's pivot_wider() function with the product column in the sample frame. However, my real columns contain numbers and characters, which yields an error when using the above sample code. The variable column is also displayed in an odder manner. Is there are workaround to this issue, or is the best way truly to manually write the code for every single plot?


Solution

  • According to your first example (I just removed your theme styling) it works like this.

    df <- data.frame(
      id = c(1:5),
      product = c("product1","product2","product1","product3","product2"),
      variable = c("var1","var1","var3","var2","var1"),
      price = c(100,120,140,90,80)
    )
    
    plot <- ggplot(df, aes(x = id, y = price, color = variable)) +
      geom_point() +
      facet_wrap(~product)
    
    ggplotly(plot)
    

    enter image description here

    The only difference is that I did not chain with %>% as it seems that plotly throws an error when I try this below:

    data.frame(
      id = c(1:5),
      product = c("product1","product2","product1","product3","product2"),
      variable = c("var1","var1","var3","var2","var1"),
      price = c(100,120,140,90,80)
    ) %>% ggplot(aes(x = id, y = price, color = variable)) +
      geom_point() +
      facet_wrap(~product) %>% ggplotly()
    
    # Error in UseMethod("ggplotly", p) : 
    #   no applicable method for 'ggplotly' applied to an object of class "c('FacetWrap', 'Facet', 'ggproto', 'gg')"
    

    Extended example with custom tooltip

    You can make it as fancy as you like, I added price formatting and for the fun of it combined product and variable as one line in the tooltip.

    custom_tooltip <- paste0("ID: ", df$id, "\n", "Product: ", df$product, " (", df$variable, ")\n", "Sold for: £ ", df$price)
    
    plot <- ggplot(df, aes(x = id, y = price, color = variable)) +
      geom_point(aes(text = custom_tooltip)) +
      facet_wrap(~product)
    
    ggplotly(plot, tooltip = c("text"))
    

    enter image description here