Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grouped boxplot data not centred on xaxis ticks, looses boxgap argument, and wont resize with subplots #1484

Open
l-jaye opened this issue Mar 10, 2019 · 5 comments · May be fixed by #1514
Open

Comments

@l-jaye
Copy link

l-jaye commented Mar 10, 2019

Hi @cpsievert!

Here after your Shiny webinar, great stuff!

I'm having issues with the boxplot data aligning with x-axis tick marks in subplots. Please see below:

library (data.table)
library (stringr)
library (plotly)
# some setup:
get_plot_title = function(this.plot.title, y.pos = 1, y.shift = 0){
  list(text = sprintf ("<b>%s</b>", this.plot.title), yshift = y.shift
       , xref = "paper", yref = "paper", yanchor = "center", xanchor = "center", align = "center"
       , x = 0.5, y = y.pos, showarrow = FALSE, font = list (size = 16, family = "Arial")
  )} 

generate_panel = function(dat, trace.name){
  my.plot = 
    plot_ly(dat, type = "box"
            , x= ~get(x_var), y= ~get(y_var), color = ~get(color_var)
            , whiskerwidth = 0.1
            , boxpoints = F
    )   %>% 
    layout(boxmode = "group", boxgroupgap = 0.1, boxgap = 0.15
           , plot_bgcolor = "#F5F5F5"
           , annotations = get_plot_title(trace.name, y.shift = 20)
           , xaxis = list(title = "", tickfont = list(size = 14, family = "Arial")
                          , tickmode = "linear", ticks = "outside")
           , yaxis = list(title = sprintf("<b>%s</b>", y_var), titlefont = list (size = 16, family = "Arial"))
           ) 
  my.plot #return
}

x_var = "cut"
y_var = "price"
color_var = "color" 
X_panel_var = "clarity"
test.dat = ggplot2::diamonds %>% setDT

Without subplots, everything looks great:

generate_panel(test.dat, trace.name = "all")

image

Adding two horizontal subplots:

test.dat[, get(X_panel_var) %>% levels][1:2] %>% 
  lapply(function(this.panel.var){
    generate_panel(dat = test.dat[this.panel.var, on = X_panel_var], trace.name = this.panel.var)
  }) %>% 
  subplot(shareY = T, shareX = F, titleX = F, nrows = 1, which_layout = 1, margin = 0.01)

Boxplot groups shift off the centre of the xaxis ticks, and the boxgap argument becomes irrelevant:
image

Enlarging this plot does not work at all (cuts off the data at the original subplot width and height, yikes!):

image

Interestingly, with 3 horizontal subplots, the middle plot data is centered around ticks, but the outer plots are even further away (and the boxgaps are worse):

test.dat[, get(X_panel_var) %>% levels][1:3] %>% 
  lapply(function(this.panel.var){
    generate_panel(dat = test.dat[this.panel.var, on = X_panel_var], trace.name = this.panel.var)
  }) %>% 
  subplot(shareY = T, shareX = F, titleX = F, nrows = 1, which_layout = 1, margin = 0.01) 

image

Just wondering if you have any ideas / suggestions?

Thank you!

@cpsievert cpsievert linked a pull request Apr 9, 2019 that will close this issue
1 task
@fabbra
Copy link

fabbra commented Apr 24, 2020

I have a similar / the same issue here:

image

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px

fig = make_subplots(rows=2, cols=1)
df = px.data.tips()

style1 = dict(name='Smokers', legendgroup='Smokers', line=dict(color='red'))
style2 = dict(name='Non-Smokers', legendgroup='Non-Smokers', line=dict(color='blue'))

fig.add_trace(go.Box(y=df[df.smoker=='Yes'].total_bill, **style1,
                     x=df[df.smoker=='Yes'].time),
                     row=1, col=1)
fig.add_trace(go.Box(y=df[df.smoker=='No'].total_bill, **style2,
                     x=df[df.smoker=='No'].time),
                     row=1, col=1)

fig.add_trace(go.Box(y=df[df.smoker=='Yes'].tip, **style1,
                     x=df[df.smoker=='Yes'].time, showlegend=False),
                     row=2, col=1)
fig.add_trace(go.Box(y=df[df.smoker=='No'].tip, **style2,
                     x=df[df.smoker=='No'].time, showlegend=False),
                     row=2, col=1)

fig.update_layout(
    boxmode='group'
)
fig.show()

Is there a workaround for this problem?

I would have expected that legendgroup would work to properly group the boxplots when using boxmode='group'.

@kmcentush
Copy link

I'm having the same problem. The code snippet provided by @fabbra is a good example.

It's interesting to note that when using plotly express w/ facet rows/cols, the boxplots are grouped properly. I haven't had a chance to dig into this yet, so I'm not sure why.

@FelipeMoser
Copy link

Hi, I know this post is a bit old but I struggled with the same problem. The solution was to put the boxes I wanted aligned into the same "offsetgroup". Using Fanbras' code:

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px

fig = make_subplots(rows=2, cols=1)
df = px.data.tips()

style1 = dict(name='Smokers', legendgroup='Smokers', line=dict(color='red'))
style2 = dict(name='Non-Smokers', legendgroup='Non-Smokers', line=dict(color='blue'))

fig.add_trace(go.Box(y=df[df.smoker=='Yes'].total_bill, **style1,
                     x=df[df.smoker=='Yes'].time, offsetgroup="A"),
                     row=1, col=1)
fig.add_trace(go.Box(y=df[df.smoker=='No'].total_bill, **style2,
                     x=df[df.smoker=='No'].time, offsetgroup="B"),
                     row=1, col=1)

fig.add_trace(go.Box(y=df[df.smoker=='Yes'].tip, **style1,
                     x=df[df.smoker=='Yes'].time, showlegend=False, offsetgroup="A"),
                     row=2, col=1)
fig.add_trace(go.Box(y=df[df.smoker=='No'].tip, **style2,
                     x=df[df.smoker=='No'].time, showlegend=False, offsetgroup="B"),
                     row=2, col=1)

fig.update_layout(
    boxmode='group'
)
fig.show()

@LukasHebing
Copy link

Is there a solution for the error in plotly express using facet_row or facet_col? The workaround to add each trace individually seems very tedious.

@mpage21
Copy link

mpage21 commented Mar 11, 2024

Still seeing this issue where grouped boxplots are being offset when two plots are combined with subplots. Because I'm working with tibbles, I don't see how I can apply the offset group suggested previously. Also, I need to be working with just plotly and not ggplotly, due to speed the plots need to be rendered at.
@cpsievert Do you have any suggestions?

image

Here is a simplified version of my code:

library(plotly)
library(tidyverse)
library(scales)

expr <- read_tsv('../path/to/file.tsv')
colorLevels <- setNames(hue_pal()(9), levels(as.factor(names(expr$site_detail))))

plot_boxpot <- function(data, value_col, colorLevels, ids) {
  p <- plot_ly(data, 
               y = ~id, 
               x = as.formula(paste0("~", value_col)), 
               color = ~site_detail,
               colors = colorLevels,
               type = 'box', 
               boxpoints = 'all',
               pointpos = 0) %>%
    layout(
      boxmode = 'group',
      boxgap = 0.2
    ) %>%
    layout(
      yaxis = list(
        tickmode = "array",
        tickvals = ~id,
        ticktext = ~id
      )
    )
}

p1 <- plot_boxpot(expr, expr_col, colorLevels, ids)
p2 <- plot_boxpot(expr, expr_col_2, colorLevels, ids)

fig <- subplot(p1, p2, nrows = 1, shareY = TRUE) %>%
    layout(
      yaxis = list(
        tickmode = "category", 
        tickvals = ids, 
        ticktext = ids),
      legend = list(
        orientation = 'h',
        xanchor = 'center', 
        yanchor = 'top'
      )
    )
fig

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants