Search code examples
pythonpandasmatplotlibbar-chartstackedbarseries

While plotting bar plot in python, how can I make it stacked for 2 columns and not stacked for one column in a pandas data frame of 3 columns?


I have a data frame with 4 columns in it. say column1, column2, column3, column4. column1 is total of column3 and column4. I want to plot a bar plot with column3 and column4 stacked, but column1 & column2 as single non stacked ones. how can I do this hybrid stacked?

here is the date frame like:

Date    column1 column2 column3 column4
    2021-08-20  19  30  11  8
    2021-08-11  15  25  11  4
    2021-08-07  5   10  5   0
    2021-08-19  25  36  16  9
    2021-08-31  6   6   6   0

I want it to look something like this, except for 1 stacked bar(column3 & column4)

enter image description here

I am trying this:

ax = final_df[['Date', 'column1', 'column2']].plot(kind = 'bar', x = 'Date', stacked = False, rot = 90, figsize = (20,5))
ax = final_df[['Date', 'column3', 'column4']].plot(kind = 'bar', x = 'Date', stacked = True, rot = 90, figsize = (20,5))

but this is obviously giving me 2 plots


Solution

  • You can plot via matplotlib, calculating the positions for each bar.

    The following example code uses a list of list to indicate which columns go together.

    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    from io import StringIO
    
    df_str = '''    Date    column1 column2 column3 column4
        2021-08-20  19  30  11  8
        2021-08-11  15  25  11  4
        2021-08-07  5   10  5   0
        2021-08-19  25  36  16  9
        2021-08-31  6   6   6   0'''
    final_df = pd.read_csv(StringIO(df_str), delim_whitespace=True)
    
    columns_to_plot = ['column1', 'column2', ['column3', 'column4']]
    
    fig, ax = plt.subplots(figsize=(20, 5))
    bar_spots = len(columns_to_plot)
    bar_width = 0.8 / bar_spots
    pos = np.arange(len(final_df))
    dodge_offsets = np.linspace(-bar_spots * bar_width / 2, bar_spots * bar_width / 2, bar_spots, endpoint=False)
    for columns, offset in zip(columns_to_plot, dodge_offsets):
        bottom = 0
        for col in ([columns] if isinstance(columns, str) else columns):
            ax.bar(pos + offset, final_df[col], bottom=bottom, width=bar_width, align='edge', label=col)
            bottom += final_df[col]
    ax.set_xticks(pos)
    ax.set_xticklabels(final_df['Date'], rotation=0)
    ax.legend()
    plt.tight_layout()
    plt.show()
    

    bar plot with some columns stacked, other not