plotly.R
plotly.R copied to clipboard
Grouped boxplot data not centred on xaxis ticks, looses boxgap argument, and wont resize with subplots
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")
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:
Enlarging this plot does not work at all (cuts off the data at the original subplot width and height, yikes!):
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)
Just wondering if you have any ideas / suggestions?
Thank you!
I have a similar / the same issue here:
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'
.
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.
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()
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.
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?
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