Search code examples
pythonplotlybar-chartoverlaygroup

Plotly: How to make grouped bargraphs on top of each other?


This is a plotly question as I wish to have the interactive graph. I am trying to make two sets of grouped bargraphs on top of each other in which one of the sets is not filled but with a thick line and the other set is filled.

This is my dummy dataset:

from numpy import random
import pandas as pd

list = []
for species in ['C1', 'C2', 'C3', 'C4']:
    for catalyst in ['CHA', 'SAPO', 'MOF','None']:
        for add in [True, False]:
            new_data = {'species': species, 
                        'catalyst': catalyst, 
                        'energy': random.uniform(low=0.0, high=1.0, size=None), 
                        'with_additive': add}
            list.append(new_data)
df = pd.DataFrame(list)            
df

Next, in plotly I manage to make bargraphs one over the other like this

fig = px.bar(df,
             x='species',
             y='energy',
             facet_row ='with_additive',
             color = 'catalyst',
             barmode ='group',
             pattern_shape="with_additive", pattern_shape_sequence=[".", ""]
            )
fig

with this result: plotly bragraphs, two separate graphs

I can also make them in the same plot, but then the two sets end up side by side as in the following:

fig = px.bar(df,
             x='species',
             y='energy',
             #facet_row ='with_additive',
             color = 'catalyst',
             barmode ='group',
             pattern_shape="with_additive", pattern_shape_sequence=[".", ""]
            )
fig

Producing this: All in one graph side by side

Then I tried to add the second set as a trace, but do not mange to make that grouped:

fig = px.bar(df[df.with_additive==True],
             x='species',
             y='energy',
             #facet_row ='with_additive',
             color = 'catalyst',
             barmode ='group',

            )

fig.add_trace(
    go.Bar(x=df[df.with_additive==True].species, y=df[df.with_additive==True].energy,
           #color = 'surface',
           alignmentgroup ='species',
           base='overlay',
        )
    )

Add a second trace, not grouped correctly

So, what I am missing is: How to get one of the sets with black line contour, and get how to the two sets perfetcly ontop of each other as in the followig matplotlib examples:


df_pivot1 = pd.pivot_table(
   df[df.with_additive==True],
   values="energy",
   index="catalyst",
   columns="species",
)

df_pivot2 = pd.pivot_table(
   df[df.with_additive==False],
   values="energy",
   index="catalyst",
   columns="species",
)

 
fig, ax = plt.subplots(figsize=(7,5)) 
df_pivot1.plot(kind="bar", ax=ax)
df_pivot2.plot(kind="bar", edgecolor='black', linewidth=2, ax=ax, fill=False)

handles, labels = ax.get_legend_handles_labels()
handle_list =handles[0:5]
label_list= labels[0:4]
label_list.append('no additive')
plt.legend(handle_list, label_list, loc='upper center', bbox_to_anchor=(0.5, 1.1), ncol=5)

As desired, but with matplotlib


Solution

  • You can do it like this:

    1. Build two figures fig1=px.bar() and fig2=px.bar()

    2. Make the necessary changes to fig2 to make the traces stand out with no color and a black line with:

     fig2.update_traces(marker_color = 'rgba(0,0,0,0)', marker_line_color = 'black')
    

    3. Change the trace names of fig2 with:

    fig2.for_each_trace(lambda t: t.update(name = t.name + ' No additives'))
    

    3. Set up a new figure fig=go.Figure(fig1.data)

    4. Add the edited traces to fig from fig2 using fig.add_traces(fig2.data)

    And that's it:

    Plot

    enter image description here

    Code:

    from numpy import random
    import pandas as pd
    import plotly.express as px
    import plotly.graph_objects as go
    
    list = []
    for species in ['C1', 'C2', 'C3', 'C4']:
        for catalyst in ['CHA', 'SAPO', 'MOF','None']:
            for add in [True, False]:
                new_data = {'species': species, 
                            'catalyst': catalyst, 
                            'energy': random.uniform(low=0.0, high=1.0, size=None), 
                            'with_additive': add}
                list.append(new_data)
    df = pd.DataFrame(list)            
    
    fig1 = px.bar(df[df.with_additive==True],
                 x='species',
                 y='energy',
                 facet_row ='with_additive',
                 color = 'catalyst',
                 barmode ='group',
                 # pattern_shape="with_additive", pattern_shape_sequence=[".", ""]
                )
    
    fig2 = px.bar(df[df.with_additive==False],
                 x='species',
                 y='energy',
                 facet_row ='with_additive',
                 color = 'catalyst',
                 barmode ='group',
                )
    
    fig2.update_traces(marker_color = 'rgba(0,0,0,0)', marker_line_color = 'black')
    fig2.for_each_trace(lambda t: t.update(name = t.name + ' No addititves'))
    
    fig = go.Figure(fig1.data)
    fig.add_traces(fig2.data)
    fig.show()