bokeh vbar_stack: positive values on positive side, negative values on negative side

Question:

Example program

import bokeh.colors
import pandas as pd
from bokeh.plotting import figure, show
import bokeh.models
import bokeh.palettes
import bokeh.transform

data = {
    'date': ['2024-04-15', '2024-04-16', '2024-04-17' ],
    'Bill': [1,  1, -1],
    'Note': [1, -1, -1],
    'Bond': [1,  0, -1]
}

df = pd.DataFrame(data)

df['date'] = pd.to_datetime(df['date'])

df.set_index('date', inplace=True)

p = figure(sizing_mode='stretch_both', x_axis_type='datetime', x_axis_label='date', y_axis_label='change')

p.vbar_stack(stackers=['Bill', 'Note', 'Bond'], x='date', width=pd.Timedelta(days=0.9), color=bokeh.palettes.Category20[3], source=df, legend_label=['Bill', 'Note', 'Bond'])

p.legend.click_policy = 'hide'

show(p)

Output

enter image description here

Notes

In the first column, all three values are positive, so they stack upwards as expected.

In the third column, all three values are negative, so they stack downwards as expected.

In the middle column, we have 1 positive value, so it stacks upward, then we have one negative value, so it then stacks downward. The result is, we end up with the appearance of a stack of height 1.

Question

Is there a way to have positive values only stack up on the positive side of the y-axis and for negative values to only show up on the negative side of the y-axis?

In other words, the chart would look as follows:

enter image description here

Asked By: dharmatech

||

Answers:

The trick is to split the DataFrame in a positiv and a negative one and draw two stackers.

Here is the code:

p = figure(x_axis_type='datetime', x_axis_label='date', y_axis_label='change')

p.vbar_stack(
    stackers=['Bill', 'Note', 'Bond'],
    x='date', width=pd.Timedelta(days=0.9),
    color=bokeh.palettes.Category20[3],
    source=df[df<0].fillna(0),
    legend_label=['Bill', 'Note', 'Bond'],
    line_width=0,
)
p.vbar_stack(
    stackers=['Bill', 'Note', 'Bond'],
    x='date', width=pd.Timedelta(days=0.9),
    color=bokeh.palettes.Category20[3],
    source=df[df>0].fillna(0),
    legend_label=['Bill', 'Note', 'Bond'],
    line_width=0,
)
p.legend.click_policy = 'hide'

show(p)

It is important to fill the selection with zeros to avoid an addtition with np.nan. I also added line_width=0, to avoid thin orange boxes on both sides of the bar.

The result looks like below:

stacker with respect of the sign

Answered By: mosc9575
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.