user171780
user171780

Reputation: 3115

Add "facet_row labels" in manually produced subplots with Plotly

Consider a figure with subplots, for example like this:

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

fig = make_subplots(rows=3, cols=1)

fig.append_trace(go.Scatter(
    x=[3, 4, 5],
    y=[1000, 1100, 1200],
), row=1, col=1)

fig.append_trace(go.Scatter(
    x=[2, 3, 4],
    y=[100, 110, 120],
), row=2, col=1)

fig.append_trace(go.Scatter(
    x=[0, 1, 2],
    y=[10, 11, 12]
), row=3, col=1)


fig.update_layout(height=600, width=600, title_text="Stacked Subplots")
fig.show()

and we obtain this

enter image description here

How do I add (manually) labels in the same way as plotly.express would place here:

enter image description here

Upvotes: 1

Views: 2717

Answers (2)

vestland
vestland

Reputation: 61164

Your question is not very clear. You're asking about labelling manually produced subplots consisting of go.Scatter() traces. And then you're requestion a solution using go.Histogram2d and Plotly Express but not fig.add_annotations(). To me, it sounds like you could have asked four very different questions. Still, to me it seems like you'd like to use px.density_hetmap() which will produce plots similar to go.Histogram2d. And then it all boils down to having the correct data structure and specifying the right facet_row labels.

Below is an example using the dataset ``px.data.tips to produce this plot:

enter image description here

And if my assumptions are right, this hould be exactly what you're aiming to do.

Complete code:

import plotly.express as px
df = px.data.tips()

fig = px.density_heatmap(df, x="total_bill", y="tip", facet_row="sex", facet_col="smoker")
fig.show()

Edit: Can you have different y-binning for each row?

Yes, I think so. Of course depending a bit on what you mean. But you can freely define the binnings of all axes through:

fig.data[n].update(xbins = {'end': 155, 'size': 5, 'start': 0}, ybins = {'end': 11, 'size': 1, 'start': 0})

Where n in this case is an element in [0,3] and corresponds to the number of subplots. You will also have to specify:

fig.update_xaxes(matches=None)
fig.update_yaxes(matches=None)

Or you'll get some very funky results.

Compare the following plot and corresponding code snippet with those above to see an example with:

fig.data[3].update(xbins = {'end': 75, 'size': 5, 'start': 0}, ybins = {'end': 25, 'size': 1, 'start': 0})

Plot 2:

enter image description here

Complete code for Plot 2:

import plotly.express as px
df = px.data.tips()

fig = px.density_heatmap(df, x="total_bill", y="tip", facet_row="sex", facet_col="smoker")
fig.update_xaxes(matches=None)
fig.update_yaxes(matches=None)
fig.data[3].update(xbins = {'end': 75, 'size': 5, 'start': 0}, ybins = {'end': 25, 'size': 1, 'start': 0})
fig.for_each_yaxis(lambda yax: yax.update(showticklabels = True))


fig.show()

Upvotes: 1

bas
bas

Reputation: 15462

If you inspect the fig.layout in the plotly express example you can see that these texts are annotations.

So what you could do is use add_annotation for each trace.

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

fig = make_subplots(rows=3, cols=1)

fig.append_trace(go.Scatter(x=[3, 4, 5], y=[1000, 1100, 1200]), row=1, col=1)
fig.append_trace(go.Scatter(x=[2, 3, 4], y=[100, 110, 120]), row=2, col=1)
fig.append_trace(go.Scatter(x=[0, 1, 2], y=[10, 11, 12]), row=3, col=1)

fig.add_annotation(
    showarrow=False,
    text="smoker=Yes",
    textangle=90,
    x=1,
    xanchor='left',
    xref="paper",
    y=0.875,
    yanchor='middle',
    yref="paper",
)

fig.add_annotation(
    showarrow=False,
    text="smoker=No",
    textangle=90,
    x=1,
    xanchor='left',
    xref="paper",
    y=0.5,
    yanchor='middle',
    yref="paper",
)

fig.add_annotation(
    showarrow=False,
    text="smoker=Maybe",
    textangle=90,
    x=1,
    xanchor='left',
    xref="paper",
    y=0.125,
    yanchor='middle',
    yref="paper",
)

fig.update_layout(
    height=600,
    width=600,
    title_text="Stacked Subplots",
    legend=dict(yanchor="top", y=1, xanchor="right", x=1.3)
)
fig.show()

In the example above the legend is moved somewhat to the right so it doesn't cover the annotations.

To shorten the code you could put the traces in a list and loop through it. For each iteration you could add the trace and the annotation where the x and y position of each annotation are relative to the row (and column).

Upvotes: 1

Related Questions