Search code examples
pythonholoviewshvplotholoviz

How to plot a stacked bar chart using hvplot?


I am trying to plot a stacked bar chart with 3 categorical variables and 1 numerical variable using hvplot.

Does anyone know how to properly plot the stacked bar chart?
Request Type 'D' & 'S' are not shown in different colors.

Data:
image of data with 3 categories and 1 numerical value

My code:

by_type = test_df.groupby(['Type', 'Fiscal Period', 'Request Type']).agg({'Count': np.sum})
plot_by_type = by_type.hvplot.bar('Type','Count', by=['Fiscal Period', 'Request Type'], stacked=False, 
                                    flip_xaxis=False, ylabel='Total Count', invert=False,
                                                     cmap=['silver', 'goldenrod'])
plot_by_type

Below is the plot I get: image of data with 3 categories and 1 numerical value


Solution

  • Currently it is not possible in HoloViews (1.13) to have more than 2 categorical variables for a barchart.

    See also this github issue:
    https://github.com/holoviz/holoviews/issues/2878

    However, you could do a workaround like this:
    The trick is to put one categorical as x, one categorical variable in the by keyword, and other categorical variables in the groupby keyword.

    import pandas as pd
    import hvplot.pandas
    
    # create sample data
    df = pd.DataFrame({
        'Type': ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'],
        'Fiscal Period': ['2019-01', '2019-01', '2019-02', '2019-02', '2019-01', '2019-01', '2019-02', '2019-02'],
        'Request Type': ['S', 'D', 'S', 'D', 'S', 'D', 'S', 'D'],
        'values': range(1, 9),
    })
    
    # create a separate barchart per Type
    layout = df.hvplot.bar(
        x='Fiscal Period', 
        y='values', 
        by='Request Type', 
        groupby='Type', 
        stacked=True, 
        cmap='Category20', 
        legend='top_left',
        width=400,
        xlabel='',
    ).layout()
    
    # make plots nicer so they look more like a clustered barchart
    plotA = layout['A'].opts(title='Type: A')
    plotB = layout['B'].opts(show_legend=False, yaxis=None, ylabel='', title='Type: B')
    
    # add separate plots together again
    (plotA + plotB).opts(title='Showing the counts per Fiscal Period, Request Type and Type')
    



    Resulting plot:

    workaround for barchart with 3 categorical variables

    As a bonus, this code will give you the same result as above:

    def create_subplot(type_selected):
        plot = df[df['Type'] == type_selected].hvplot.bar(
            x='Fiscal Period', 
            y='values', 
            by='Request Type', 
            stacked=True, 
            cmap='Category20', 
            label='Type: ' + type_selected,
            legend='top_left',
            width=400,
            xlabel='',
            ylabel='',
        )
        return plot
    
    plotA = create_subplot('A')
    plotB = create_subplot('B').opts(show_legend=False, yaxis=None)
    
    (plotA + plotB).opts(title='Showing the counts per Fiscal Period, Request Type and Type')