Can you create overlaying barcharts with the same x value as index

Hi all,

I wanted to created this chart:


and I got stuck with the overlay part.

In the end I used the Previous Year chart as a background-image for the Current chart (AC).
PY is generated when the image does not already exist, it took some experimenting.

I tried a lot to avoid this route, and still wonder, how can you accomplish an effect like this without using a background image and by creating two barcharts. Previous Year does not need tooltips. I tried stuff like a little shift for the previous year, to keep the current year exactly at the right spot on the x-axis.

code for this graph

def create_bar_ac_py(df_revenue_mom):  
    
    colors = {'AC': 'rgba(64,64,64,1)','PY':'rgba(166,166,166,1)' }
    # Define a small shift using Timedelta
    #shift = pd.Timedelta(days=3)  # Adjust as needed
    
    max_y = df_revenue_mom['revenue'].max()
    current_year = df_revenue_mom['Last_dayofmonth_Date'].dt.year.max()
    src_background = f"/assets/{current_year}_figPY.svg"

    
    
    figPY = go.Figure()
    
    
    
    # First bar trace (shift left)
    figPY.add_trace(go.Bar(
               x=df_revenue_mom['Last_dayofmonth_Date'],
               y=df_revenue_mom['revenue_py'], 
               #width=0.5,
               marker_color=colors.get('PY'),
               name='PY'
    ))
    
    #background needs to be transparent
    figPY.update_layout({
        'plot_bgcolor': 'rgba(0, 0, 0, 0)',
        'paper_bgcolor': 'rgba(0, 0, 0, 0)',
    })
    
    
    
    # Change the bar mode
    figPY.update_layout(bargap=0.2)
    figPY.update_layout(yaxis_title=None)
    figPY.update_yaxes(showticklabels=False)
    figPY.update_layout(xaxis_title=None)
    figPY.update_xaxes(showticklabels=False)
    figPY.update_layout(yaxis_range=[0,max_y])
    figPY.update_layout(
        margin=dict(l=0, r=0, t=0, b=0),
        width=800,
        height=300,
    )
    
    #solution stuck kaleido 2 on win11 : https://github.com/plotly/Kaleido/issues/126
    #writing an image dynamically triggers the callback function for a second time
    #pio.write_image(figPY, "assets/figPY.svg") 
    
    figAC = go.Figure()
    
    # Second bar trace (shift right)
    figAC.add_trace(go.Bar(
               x=df_revenue_mom['Last_dayofmonth_Date'], 
               y=df_revenue_mom['revenue'],  
               #width=0.5,
               marker_color=colors.get('AC'),
               name='AC',
               text=df_revenue_mom['revenue'],
               texttemplate = "%{y:,.3s}"
    ))
    
    
    
    
    # Change the bar mode
    #figAC.update_layout(bargap=0.2)
    
    #background needs to be transparent fo a see through to PY
    figAC.update_layout({
         'plot_bgcolor': 'rgba(0, 0, 0, 0)',
         'paper_bgcolor': 'rgba(0, 0, 0, 0)',
     })
    
    figAC.update_xaxes(showgrid=False, zeroline=False)
    figAC.update_yaxes(showgrid=False, zeroline=False)
    
    # Add images
    figAC.add_layout_image(
            dict(
                source=src_background,
                xref="paper", yref="paper",
                #x=1, y=1,
                x=-0.01, y=0, 
                sizex=1,
                sizey=1,
                xanchor="left",
                yanchor="bottom",
                opacity=1,
                layer="below",
                sizing="stretch")
    )
    
    figAC.update_layout(yaxis_title=None)
    figAC.update_yaxes(showticklabels=False)
    figAC.update_xaxes(
        dtick="M1",
        tickformat="%b")
        #ticklabelmode="period")
        
    figAC.update_layout(
        margin=dict(l=0, r=0, t=0, b=0),
        autosize=False,
        width=800,
        height=300,
        hovermode=False
    )
    figAC.update_traces(textfont_size=12, textangle=0, textposition="outside", cliponaxis=False)
    


    return dcc.Graph(figure = figAC)

Hey I’m not sure how to do this in plotly but I’ve been procrastinating making some vega gant charts and stumbled across the attached links that might be helpful.

Bar chart variance

I’ve managed to recreate some of the gant charts using dash-vega-components and they work! So I’m guessing the same could be true for the charts you want!

1 Like

In this case, two new x-axis arrays are prepared and placed on each. We can then give them the x-axis ticks they need as strings. I customized the Gapminder sample data appropriately.

import plotly.express as px
import plotly.graph_objects as go

data_canada = px.data.gapminder().query("country == 'Canada'")
data_argentina = px.data.gapminder().query("country == 'Argentina'")

colors = {'AC': 'rgba(64,64,64,1)','PY':'rgba(166,166,166,1)' }
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
offset = 0.1

fig = go.Figure()

fig.add_trace(go.Bar(
    x=[x for x in range(0,12,1)],
    y=data_canada['pop'],
    width=0.5,
    marker=dict(color=colors.get('PY')),
    name='PY'
))

fig.add_trace(go.Bar(
    x=[x+offset for x in range(0,12,1)],
    y=data_argentina['pop'],
    width=0.5,
    text=data_argentina['pop'],
    texttemplate='%{y:,.3s}',
    textposition='outside',
    marker=dict(color=colors.get('AC')),
    name='AC'
))
fig.update_xaxes(tickmode='array', tickvals=[x+offset for x in range(0,12,1)], ticktext=months)

fig.show()

1 Like

@Javadabbadoo , thank you for your answer and your solution does what I want, at least the example shows that it can. Two buts :slight_smile: , @r-beginners has a more native solution which I’m now going to try (and will work I suppose), and I did some experimenting with vega & vega lite in Powerbi (Deneb extension) , you can do everything but you have to define all your visuals as json instead of the more native approach and if possible I go for native. Maybe that’s different with dash-vega but it still occurs to me as making things more difficult than they need to be. Maybe I’m wrong.

Thank you @r-beginners , this is exactly what I was looking for!

1 Like

Hey! You are not wrong! Hence my procrastination :joy:.

I only explored it as I liked some of the gantt charts. Everything else was a bit of a pain to do / get working and much prefer staying in plotly etc!

1 Like