pythonplotly

Can I add color gradients to plotly stacked bar chart?


I have a px stacked bar chart of 20 journal titles across the x-axis and the breakdown of access types within each journal as stacked segments. The access type data is across multiple years. Is it possible to encode both the access types AND the year by color? If possible, I would like to add gradients of the color code to represent years.

MWE

import pandas as pd
import plotly.express as px

d = {'Journal': ['Journal One', 'Journal One', 'Journal One', 'Journal One', 'Journal One', 'Journal One', 'Journal One'],
     'Access': ['Green', 'Green', 'Closed', 'Green', 'Closed', 'Green', 'Closed'],
     'Year': [2020, 2021, 2021, 2022, 2022, 2023, 2023],
     'Count': [1, 1, 5, 6, 3, 2, 4]}
df = pd.DataFrame(data=d)

fig = px.bar(df, x='Journal', y='Count', color='Access', text_auto=True,
            category_orders={'Access': ["Closed", "Green"]},
            color_discrete_map={
                "Closed": "#AB63FA",
                "Green": "#2CA02C"},
           title = f'Journal Titles by Access status and Year' )

fig.show()

dataframe

Stacked bar chart with multiple green and purple segments

Am I able to specify gradients or transparency levels for the green and purple by Year to help distinguish the different data within color blocks? Is it possible to add two color encodings? Or is there a better way to show all this?


Solution

  • I believe there is only one uniform transparency setting in plotly.express. It is possible to extract by attribute in a graph object and then further graph it by year in a loop process. The transparency is set and then subtracted in a loop process. Please modify this to what you intend. Also, I'm making duplicate legend items into a single item.

    import plotly.graph_objects as go
    
    fig = go.Figure()
    
    for a in df['Access'].unique():
        dfa = df.query('Access == @a')
        opacity = 1.0
        colors = '#2CA02C' if a == 'Green' else '#AB63FA'
        for y in dfa['Year'].unique():
            dff = dfa.query('Year == @y')
            opacity -= 0.1
            fig.add_trace(go.Bar(
                x=dff['Journal'],
                y=dff['Count'],
                marker=dict(color=colors, opacity=opacity),
                name=a,
                text=dff['Count'],
                legendgroup=a,
            ))
    
    # Make duplicate legend items unique
    names = set()
    fig.for_each_trace(
        lambda trace:
            trace.update(showlegend=False)
            if (trace.name in names) else names.add(trace.name))
    
    fig.update_layout(legend_title_text='Access', height=500, barmode='stack')
    
    fig.show()
    

    enter image description here