LINE secondary Y on a BAR chart - HOW TO ALIGN THE LINES TO FIT THE BARS?

Hey guys,

I’m trying to build a bar chart with a line secondary y chart but I’m facing problems to find a way to create a figure with the below design:
image



I did work on this and I’m a little close to finishing this, but I’m not understanding how to align the lines in the correct bar as you can see on the below chart:

image



The reproducible code is below, and any help on how to fix this will be very welcome:

import plotly.express as px
import pandas as pd
from plotly.subplots import make_subplots

mockup_data={"revenue":[250, 130, 198, 125, 180, 130], 
             "profit":[35, 22, 28, 20, 25, 23], 
             "year":[2018, 2018, 2019, 2019, 2020, 2020], 
             "company":["you", "benchmark", "you", 
                        "benchmark", "you", "benchmark", ]}

chart_data=pd.DataFrame.from_dict(mockup_data)

fig = make_subplots(specs=[[{"secondary_y": True}]])

bar_chart_fig = px.bar(chart_data, 
                     x="year", y="revenue", 
                     color_discrete_map={
                            "you": "#2C9F1B",
                            "benchmark": "#143DCB"},
                     color="company", barmode="group")

line_chart_fig=px.line(chart_data, 
                       x="year", y="profit", 
                       color="company", 
                       color_discrete_map={
                            "you": "#D2EAFF",
                            "benchmark": "#CAF0E7"}
                      )

line_chart_fig.update_traces(line=dict(width=5), mode='lines+markers', marker=dict(size=15))

fig.add_trace(bar_chart_fig.data[0],
              secondary_y=False,
            )

fig.add_trace(line_chart_fig.data[0],
                secondary_y=True
            )

fig.add_trace(bar_chart_fig.data[1],
              secondary_y=False,
            )

fig.add_trace(line_chart_fig.data[1],
                secondary_y=True
            )
        
fig.update_layout(showlegend=False,
                  height=350, width=650,
                  margin=dict(l=15, r=15, b=8, t=8), 
                      bargap=.4,bargroupgap=0.2
                 )

fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='#CED3E2',
                 tickfont=dict(color='#3E4453', size=12), zerolinecolor='#CED3E2')
fig.update_yaxes(showgrid=False, gridwidth=1, gridcolor='#CED3E2',
                              tickfont=dict(color='#3E4453', size=12), secondary_y=True)

fig.update_xaxes(showgrid=False, type='category')

Thank you very much in advance

Hi @kabure

You can align the scatter plot by creating a secondary x axis and calculating the middle of each bar. Here is an example - and as a bonus, it’s a Dash app.

import dash
import dash_core_components as dcc
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd


mockup_data = {
    "revenue": [250, 130, 198, 125, 180, 130],
    "profit": [35, 22, 28, 20, 25, 23],
    "year": [2018, 2018, 2019, 2019, 2020, 2020],
    "company": ["you", "benchmark", "you", "benchmark", "you", "benchmark",],
}

df = pd.DataFrame.from_dict(mockup_data)

app = dash.Dash()

fig = px.bar(
    df,
    x="year",
    y="revenue",
    color_discrete_map={"you": "#2C9F1B", "benchmark": "#143DCB"},
    color="company",
    barmode="group",
)

fig.layout.xaxis2 = go.layout.XAxis(overlaying="x", range=[0, 3], showticklabels=False)
fig.layout.yaxis2 = go.layout.YAxis(overlaying="y", side="right")

bargap = 0.4

dff = df[df.company == 'you']
fig.add_scatter(
    x=[i + (bargap / 2 + (1 - bargap) / 4) for i in range(3)],
    y=[dff.profit.iloc[i] for i in range(3)],
    xaxis="x2",
    yaxis="y2",
)

dff = df[df.company == 'benchmark']
fig.add_scatter(
    x=[i + (1 - bargap / 2 - (1 - bargap) / 4) for i in range(3)],
    y=[dff.profit.iloc[i] for i in range(3)],
    xaxis="x2",
    yaxis="y2",
)


fig.update_layout(
    showlegend=False,
    height=350,
    width=650,
    margin=dict(l=15, r=15, b=8, t=8),
    bargap=bargap,
    bargroupgap=0.2,
)

app.layout = dcc.Graph(figure=fig)

if __name__ == "__main__":
    app.run_server(debug=True)


image

2 Likes

Hey bro, you’re a genius!!! Thank you very much for the help.

What you did here is beyond my knowledge so far, so I will try to better understand what’s the logic behind it, but I loved the results heheh

Haha I guess I can be a bro today :woman:

I adapted the formula for finding the center of the bar from @jmmease 's post: Grouped bar charts with corresponding line chart

Glad it worked for you!

4 Likes

Hey, @AnnMarieW I’m really sorry, dear, I wrongly assumed you were a man! It’s always cool to see women on the dash community =)

Thank you very much for all your hard work supporting the community;

Regards,
Leonardo