2

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

Sander van den Oord
  • 10,986
  • 5
  • 51
  • 96
david
  • 23
  • 3

1 Answers1

1

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')
Sander van den Oord
  • 10,986
  • 5
  • 51
  • 96
  • I am wondering how come PlotB is always empty in the side-by-side plot when I run the code shown above. Individual plots are fine. I am using the latest github version of HoloViews. – david Jan 31 '20 at 03:27
  • Hi David, trying to help here. Are you using my sample data? Is it empty in both code examples above? Is it at some point not empty, for example before making the plots nice? Did you install latest versions like this: https://stackoverflow.com/questions/59363729/install-the-latest-git-versions-of-holoviews-hvplot-panel-datashader-and-para – Sander van den Oord Jan 31 '20 at 06:22
  • Hi Sander, I am using both sample codes you provided and both have plotB empty. I have installed the latest versions of HoloViews and hvPlot. However, both your code samples work on another dataset. I appreciate your help. I learned a lot from your workaround. – david Jan 31 '20 at 06:48
  • Weird... because both plotA and plotB are created in exactly the same way... So it works on your own data? But not on my sample data? Because I just tried again to be sure and I see both plots. If I try create_subplot('C') then it stays empty, because that category C does not exist. – Sander van den Oord Jan 31 '20 at 06:53