Johannes Wiesner
Johannes Wiesner

Reputation: 1307

Drawing custom error bars when using plotly subplots

This question is closely related to an earlier one that I posted. I would like to draw confidence intervals for each bar within subplots of a figure, using the information from two columns in my data frame describing the upper and lower limit of each confidence interval. I tried to use the solution from that earlier post, but it does not seem to be applicable when one wants to use different colors and/or different rows in order to draw subplots for the figure.

For example, the following code does not produce the right confidence intervals. For instance, the CI of the 3rd bar in the second row should go from 11 to 5:

import pandas as pd
import plotly.express as px


df = pd.DataFrame(
    {"x": [0, 1, 2, 3, 0, 1, 2, 3],
     "y": [6, 10, 2, 5, 8, 9, 10, 11],
     "ci_upper": [8, 11, 2.5, 4, 9, 10, 11, 12],
     "ci_lower": [5, 9, 1.5, 3, 7, 6, 5, 10],
     "state": ['foo','foo','foo','foo','bar','bar','bar','bar'],
     "color": ['0','0','1','1','0','0','1','1']}
)


fig = px.bar(df, x="x", y="y",facet_row='state',color='color').update_traces(
    error_y={
        "type": "data",
        "symmetric": False,
        "array": df["ci_upper"] - df["y"],
        "arrayminus": df["y"] - df["ci_lower"],
    }
)


fig.update_yaxes(dtick=1)
fig.show(renderer='png')

enter image description here

Upvotes: 1

Views: 760

Answers (1)

Rob Raymond
Rob Raymond

Reputation: 31226

  • it's the same technique but solution needs to consider it's multiple traces (4 in this example)
  • encoded in hovertemplate of each trace are the facet and color. Extract these and filter data down to appropriate rows
  • then build instruction for error bars as with simpler condition
import pandas as pd
import plotly.express as px


df = pd.DataFrame(
    {
        "x": [0, 1, 2, 3, 0, 1, 2, 3],
        "y": [6, 10, 2, 5, 8, 9, 10, 11],
        "ci_upper": [8, 11, 2.5, 4, 9, 10, 11, 12],
        "ci_lower": [5, 9, 1.5, 3, 7, 6, 5, 10],
        "state": ["foo", "foo", "foo", "foo", "bar", "bar", "bar", "bar"],
        "color": ["0", "0", "1", "1", "0", "0", "1", "1"],
    }
)


fig = px.bar(df, x="x", y="y", facet_row="state", color="color")
fig.update_yaxes(dtick=1)

def error_facet(t):
    # filter data frame based on contents of hovertemplate
    d = df.query(
        " and ".join(
            [
                f"{q.split('=')[0]}==\"{q.split('=')[1]}\""
                for q in t.hovertemplate.split("<br>")[0:2]
            ]
        )
    )
    t.update(
        {
            "error_y": {
                "type": "data",
                "symmetric": False,
                "array": d["ci_upper"] - d["y"],
                "arrayminus": d["y"] - d["ci_lower"],
            }
        }
    )
fig.for_each_trace(error_facet)


fig

enter image description here

Upvotes: 2

Related Questions