VBNub
VBNub

Reputation: 33

How to format plotly legend when using marker color?

I want to follow up on this post: Plotly: How to colorcode plotly graph objects bar chart using Python?.

When using plotly express, and specifying 'color', the legend is correctly produced as seen in the post by vestland.

This is my plotly express code:

data = {'x_data': np.random.random_sample((5,)),
        'y_data': ['A', 'B', 'C', 'D', 'E'],
        'c_data': np.random.randint(1, 100, size=5)
       }

df = pd.DataFrame(data=data)

fig = px.bar(df,
             x='x_data',
             y='y_data',
             orientation='h',
             color='c_data',
             color_continuous_scale='YlOrRd'
            )
fig.show()

But when using go.Bar, the legend is incorrectly displayed as illustrated here:

enter image description here

This is my code using graph objects:

bar_trace = go.Bar(name='bar_trace',
               x=df['x_data'],
               y=df['y_data'],
               marker={'color': df['c_data'], 'colorscale': 'YlOrRd'},
               orientation='h'
              )

layout = go.Layout(showlegend=True)

fig = go.FigureWidget(data=[bar_trace], layout=layout)

fig.show()

I'm learning how to use FigureWidget and it seems it can't use plotly express so I have to learn how to use graph objects to plot. How do I link the legend to the data such that it works like the plotly express example in vestland's post.

Upvotes: 0

Views: 5179

Answers (1)

Rob Raymond
Rob Raymond

Reputation: 31226

This really comes down to understanding what a high level API (plotly express) does. When you specify color in px if it is categorical it creates a trace per value of categorical. Hence the below two ways of creating a figure are mostly equivalent. The legend shows an item for each trace, not for each color.

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import numpy as np

df = pd.DataFrame({"x":np.linspace(0,10,10), "y":np.linspace(5,15,10), "color":np.random.choice(list("ABCD"),10)})

px.bar(df, x="x", y="y", color="color", orientation="h").show()

fig = go.Figure()
for g in df.groupby("color"):
    fig.add_trace(go.Bar(x=g[1]["x"], y=g[1]["y"], name=g[0], orientation="h"))

    
fig

supplementary based on comments

  • you do not have to use graph objects if you are using FigureWidget() as demonstrated by second figure, create with plotly express and then generate FigureWidget()
  • for continuous data normal pattern is to use a single trace and a colorbar (also demonstrated in second figure). However if you want a discrete legend, create a trace per value in c_data and use https://plotly.com/python-api-reference/generated/plotly.colors.html sample_colorscale()
import plotly.express as px
import plotly.colors
import plotly.graph_objects as go
import numpy as np
import pandas as pd

# simulate data frame...
df = pd.DataFrame(
    {
        "x_data": np.linspace(0, 10, 10),
        "y_data": np.linspace(5, 15, 10),
        "c_data": np.random.randint(0, 4, 10),
    }
)

# build a trace per value in c_data using graph objects ... correct legend !!??
bar_traces = [
    go.Bar(
        name="bar_trace",
        x=d["x_data"],
        y=d["y_data"],
        marker={
            "color": plotly.colors.sample_colorscale(
                "YlOrRd",
                d["c_data"] / df["c_data"].max(),
            )
        },
        orientation="h",
    )
    for c, d in df.groupby("c_data")
]

layout = go.Layout(showlegend=True)

fig = go.FigureWidget(data=bar_traces, layout=layout)
fig.show()

fig = px.bar(
    df,
    x="x_data",
    y="y_data",
    color="c_data",
    orientation="h",
    color_continuous_scale="YlOrRd",
)
fig = go.FigureWidget(data=fig.data, layout=fig.layout)
fig.show()

enter image description here

Upvotes: 2

Related Questions