2
votes

I am trying to subplot my box plots using plotly so that boxes of the same color line up with each other on the x-axis. However, when I separate them using boxmode = group:

library(plotly)
library(tidyverse)

df <- data.frame(
  w = rep(1:3, times = 2, each = 60),
  x = rep(c("A", "B", "C"), times = 20),
  y = rep(c("D", "E", "F"), each = 20),
  z = rnorm(120)
)

p <- function(val) {
  filter(df, w == val) %>% 
    plot_ly(x = ~x, y = ~z, color = ~y, type = "box") %>% 
    layout(boxmode = "group")
} 

subplot(lapply(unique(df$w), p), nrows = 3, shareX = TRUE)

This doesn't occur when using ggplot:

(ggp <- ggplot(df, aes(x = x, y = z, color = y)) +
  geom_boxplot() +
  facet_wrap(. ~ w, nrow = 3))

But it does occur once again if I try pass this plot to ggplotly():

ggplotly(ggp) %>% 
  layout(boxmode = "group")

Removing boxmode = "group" causes boxes of different colors to stack over each other, which is even worse! I've tried some alterations using alignmentgroup & offsetgroup but they appear to override the boxmode argument and cause everything to stack again. Is there anything else I can do to generate an interactive plot that keeps each box in their lane?

There are similar issues here but I don't think this solves my issue.

1

1 Answers

1
votes

Being used to ggplot2 this feels quite awkward but it works. The trick is to add the boxplots for the single groups as separate traces and to set the offsetgroup. Try this:

library(plotly)
library(tidyverse)

df <- data.frame(
  w = rep(1:3, times = 2, each = 60),
  x = rep(c("A", "B", "C"), times = 20),
  y = rep(c("D", "E", "F"), each = 20),
  z = rnorm(120)
)

p <- function(val) {
  df <- filter(df, w == val)
  # 1. Step by step
  # plot_ly(x = ~x, y = ~z, color = ~y) %>% 
  #   #purrr::reduce(unique(df$y), ~ add_trace(.y, data = filter(df, y == .x), type = "box", offsetgroup = .x))
  #   add_trace(data = filter(df, y == "D"), type = "box", offsetgroup = "D") %>%
  #   add_trace(data = filter(df, y == "E"), type = "box", offsetgroup = "E") %>%
  #   add_trace(data = filter(df, y == "F"), type = "box", offsetgroup = "F")

  # 2. using purrr::reduce
  purrr::reduce(unique(df$y), function(.x, .y) {
    add_trace(.x, data = filter(df, y == .y), type = "box", 
              offsetgroup = .y)
    }, .init = plot_ly(x = ~x, y = ~z, color = ~y))
} 

subplot(lapply(unique(df$w), p), nrows = 3, shareX = TRUE) %>% 
  layout(boxmode = "group")

Gives me this plot:

enter image description here