Search code examples
plotlybar-chartcustomizationplotly-python

How to set custom labels for x axis in a bar chart plotly figure?


I'm stuck trying to implement a customized bar chart and as I'm not finding references about how to implement it;

The problem: I need to set a custom value to each of my x-axes, as shown in the image below: enter image description here

But as shown below in my version of the chart, I'm not setting the x correctly. enter image description here


I would like to know if someone has already implemented this type of chart and could tell me how to set it correctly. Below I set a minimal reproducible code to be easier for to someone reproduce it... Any help will be really appreciated

Data:

data={'period': ['2019_1', '2019_1', '2020_1', '2020_1', '2019_2', '2019_2', '2020_2', '2020_2', 
                 '2019_3', '2019_3', '2020_3', '2020_3', '2019_4', '2019_4', '2020_4', '2020_4'], 
      'indicator': ['metric1', 'metric0', 'metric1', 'metric0', 'metric1', 'metric0', 'metric1', 
                    'metric0', 'metric1', 'metric0', 'metric1', 'metric0', 'metric1', 'metric0', 
                    'metric1', 'metric0'], 
      'value_plot': [467.2, 1453.6, 468.13, 1521.29, 490.08, 1500.02, 518.69, 1599.33,
                     480.01, 1473.12, 510.53, 1556.38, 532.57, 1751.75, 563.62, 1877.21], 
      'qtr': ['Q1', 'Q1', 'Q1', 'Q1', 'Q2', 'Q2', 'Q2', 'Q2', 'Q3',
              'Q3', 'Q3', 'Q3', 'Q4', 'Q4', 'Q4', 'Q4'], 
      'year': ['2019', '2019', '2020', '2020', '2019', '2019', '2020', 
               '2020', '2019', '2019', '2020', '2020', '2019', 
               '2019', '2020', '2020']}

Reproducible code:

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

plot_df=pd.DataFrame.from_dict(data)

bargap=.3
past_rev_color="red"
current_rev_color="blue"

current_year_mask=np.where(plot_df.year==2020, True, False)
metric0_mask=plot_df.indicator.str.endswith("metric0")

metric0_table=plot_df[metric0_mask]

fig=px.bar(metric0_table, 
           x="qtr", y="value_plot", 
           color="year", barmode="group",
            color_discrete_map={"2019": past_rev_color, 
                                "2020": current_rev_color},
           custom_data=[metric0_table["year"]])


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

metric1_table=plot_df[np.logical_not(metric0_mask)]
metric1_curr_yr_mask=np.where(metric1_table["year"]=="2020", True, False)

fig.add_scatter(
    x=[i + (bargap / 2 + (1 - bargap) / 4) for i in range(4)],
    y=[metric1_table[metric1_curr_yr_mask].value_plot.iloc[i] for i in range(4)],
    xaxis="x2",
    yaxis="y2",
    name="previous")


fig.add_scatter(
    x=[i + (1 - bargap / 2 - (1 - bargap) / 4) for i in range(4)],
    y=[metric1_table[np.logical_not(metric1_curr_yr_mask)].value_plot.iloc[i] for i in range(4)],
    xaxis="x2",
    yaxis="y2",
    name="current")

fig.update_layout(showlegend=False)

fig

Thank you all guys!!

Solution

  • import plotly.graph_objects as go
    import pandas as pd
    
    data={'period': ['2019_1', '2019_1', '2020_1', '2020_1', '2019_2', '2019_2', '2020_2', '2020_2', 
                     '2019_3', '2019_3', '2020_3', '2020_3', '2019_4', '2019_4', '2020_4', '2020_4'], 
          'indicator': ['metric1', 'metric0', 'metric1', 'metric0', 'metric1', 'metric0', 'metric1', 
                        'metric0', 'metric1', 'metric0', 'metric1', 'metric0', 'metric1', 'metric0', 
                        'metric1', 'metric0'], 
          'value_plot': [467.2, 1453.6, 468.13, 1521.29, 490.08, 1500.02, 518.69, 1599.33,
                         480.01, 1473.12, 510.53, 1556.38, 532.57, 1751.75, 563.62, 1877.21], 
          'qtr': ['Q1', 'Q1', 'Q1', 'Q1', 'Q2', 'Q2', 'Q2', 'Q2', 'Q3',
                  'Q3', 'Q3', 'Q3', 'Q4', 'Q4', 'Q4', 'Q4'], 
          'year': ['2019', '2019', '2020', '2020', '2019', '2019', '2020', 
                   '2020', '2019', '2019', '2020', '2020', '2019', 
                   '2019', '2020', '2020']}
    
    plot_df=pd.DataFrame.from_dict(data)
    metric0_mask=plot_df.indicator.str.endswith("metric0")
    
    metric0_table=plot_df[metric0_mask]
    df_line = plot_df[~metric0_mask]
    past_rev_color="red"
    current_rev_color="blue"
    
    
    fig = go.Figure(
        go.Bar(
            x=[metric0_table["qtr"], metric0_table["year"]],
            y=metric0_table["value_plot"],
            marker={
                "color": metric0_table["year"].map(
                    {"2019": past_rev_color, "2020": current_rev_color}
                )
            },
        )
    ).add_traces(
        [
            go.Scatter(
                x=[
                    df_line.loc[df_line["year"].eq(y), "qtr"],
                    df_line.loc[df_line["year"].eq(y), "year"],
                ],
                y=df_line.loc[df_line["year"].eq(y), "value_plot"],
                yaxis="y2"
            )
            for y in df_line["year"].unique()
        ]
    ).update_layout(yaxis2={"overlaying":"y","side":"right"}, showlegend=False)
    
    fig
    
    
    

    enter image description here