Hello!
Below is provided a code snippet generating a chart with 3 subplots from a px dataset. I’m struggling to position the legend names accurately. When digging into the rendered page, I can’t figure out what CSS/SVG property I have to trick to achieve the desired output. At first, it looks like we miss a features like “tracegroupgap”, to set the horizontal spacing between each legend groups. The goal is to position every legendgroup at the bottom right corner of each subplot.
The code:
import plotly.graph_objects as go
import plotly.express as px
df = px.data.tips()
fig =go.Figure()
# fig6 = make_subplots(rows=1, cols=6, shared_yaxes="rows")
fig.add_trace(go.Bar(
x=df.groupby(["day"])["total_bill"].min(), y=df.groupby(["day"])["total_bill"].sum().index,
marker=dict(color='rgba(120,255,186, 0.6)'), orientation="h", name="traceA_One", xaxis="x", yaxis="y",
legendgroup=1, legendrank=2, offset=-0.5, offsetgroup="1",
))
fig.add_trace(go.Bar(
x=df.groupby(["day"])["total_bill"].max(), y=df.groupby(["day"])["total_bill"].sum().index,
marker=dict(color='rgba(180,255,186, 0.6)'), orientation="h", name="traceA_Two", xaxis="x", yaxis="y",
legendgroup=1, legendrank=2, offset=-0.3, offsetgroup="2",
))
fig.add_trace(go.Bar(
x=df.groupby(["day"])["total_bill"].mean(), y=df.groupby(["day"])["total_bill"].sum().index,
marker=dict(color='rgba(180,255,186, 0.6)'), orientation="h", name="traceB_One", xaxis="x2", yaxis="y",
legendgroup=2,
))
fig.add_trace(go.Bar(
x=df.groupby(["day"])["total_bill"].median(), y=df.groupby(["day"])["total_bill"].sum().index,
marker=dict(color='rgba(150,148,186, 0.6)'), orientation="h", name="traceC_One", xaxis="x3", yaxis="y",
legendgroup=3,
))
fig.update_layout(
xaxis=dict(domain=[0.0, 0.32]), xaxis2=dict(domain=[0.34, 0.66]), xaxis3=dict(domain=[0.68, 1]),
yaxis=dict(anchor="x", domain=[0.0, 0.98]), margin=dict(l=10, r=10, b=0, t=40), barmode="overlay", bargap=0.3,
legend=dict(
yanchor="bottom", y=0, xanchor="left", x=0, orientation="h", itemsizing="constant", borderwidth=0,
bgcolor="rgba(100,100,100,0.25)", itemwidth=30, entrywidthmode="fraction", entrywidth=0.33, tracegroupgap=0
),
)
fig.show()
Each subplot has a domain of length (1-(nbrOfSubplot-1)*gapLength)/nbrOfSubplots. So, in my case:
- 1st domain goes from 0 to 0.32, then there is a gap of 0.02,
- 2nd domain goes from 0.34 to 0.66, then there is a gap of 0.02
- 3rd domain goes from 0.68 to 1
My “legend area” contains legendgroups that are displayed horizontally. Within each legendgroup, legend names are displayed vertically.
The central part of the issue lies in this:
legend=dict(
yanchor="bottom", y=0, xanchor="left", x=0, orientation="h",
itemsizing="constant", itemwidth=50,
entrywidthmode="fraction", entrywidth=0.32,
bgcolor="rgba(100,100,100,0.25)",
tracegroupgap=0, borderwidth=0
),
With a tracegroupgap = 0 and an entrywidth of 0.32, the first legendgroup does have a width of 0.32. It ends at the end of the first xaxis domain. However, the 3 legend group are not displayed side by side: there is a gap between each of them:
On the above screenshot, I’ve highlighted the legend name zone by adding a SVG rect via the browser’s inspection tool, prior to take the screenshot.
If I set the entrywidth to 0.32+0.02, then I get this:
Again, legends names area are highlighted in dark grey/light red via the browser inspection tool
The last legendgroup is pushed on a new “row”, probably because 0.34*3>1
Since the tracegroupgap is set on 0, there is no space between the first group of legend names, and the third group. Should the tracegroupgap be 50, then there would be 50px white space.
Therefore, to achieve the desired output, I would need to
- set the entrywidth to 0.32
- define a kind of “horizontal” trace group gap of 0.02 (fraction, not pixel)
This way the first legendgroup would span from 0 to 0.32, then there would be a gap of 0.02, etc.
Is there a workaround to do that by removing the default value of the horizontal gap, maybe?