Geoff Ruddock

Stacked area chart

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import bokeh
Package     Version
----------  ---------
python      3.8.8
matplotlib  3.4.3
seaborn     0.11.1
bokeh       2.3.2
import random
from IPython.core.display import HTML
from bokeh.plotting.figure import Figure
from bokeh.plotting import figure, show, reset_output, save
from bokeh.io import output_notebook, output_file

output_notebook(hide_banner=True)

def hugo_safe_render(fig: Figure) -> HTML:
    """ Save bokeh plot as HTML and re-embed into notebook, so that it also renders in Hugo markdown. """
    reset_output()
    name = f'{random.getrandbits(32)}.html'
    output_file(name)
    _ = save(fig)
    
    return HTML(f'<embed type="text/html" src="{name}" width="100%" height="{int(fig.height+100)}"></embed>')

Stacked area chart

Generate dummy data

x = pd.date_range('2020-01-01', '2020-01-31')
y = np.sin(range(len(x)))

df = pd.DataFrame({
    'sin': np.sin(range(len(x))) + 1,
    'cos': np.cos(range(len(x))) + 1,
    '2sin': (np.sin(range(len(x))) + 1) * 2,
}, index=x)

df.head()

sin cos 2sin
2020-01-01 1.000000 2.000000 2.000000
2020-01-02 1.841471 1.540302 3.682942
2020-01-03 1.909297 0.583853 3.818595
2020-01-04 1.141120 0.010008 2.282240
2020-01-05 0.243198 0.346356 0.486395

pandas

df.plot.area();

png

bokeh

from bokeh.palettes import brewer
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.models import DataRange1d, HoverTool, DatetimeTickFormatter

output_notebook(hide_banner=True)

fig = figure(
    plot_height=200,
    sizing_mode='scale_width',
    x_axis_type='datetime',
    # get rid of gap on x-axis
    x_range=DataRange1d(start=x.min(), end=x.max())
)

names = df.columns.tolist()

fig.varea_stack(
    stackers=names,
    x='index',
    color=brewer['Spectral'][3],
    legend_label=names,
    source=df
)

# workaround for using HoverTool → https://stackoverflow.com/a/60895524/4866803
fig.vline_stack(
    stackers=names,
    x='index',
    color=brewer['Spectral'][3],
    legend_label=names,
    source=df
)

fig.grid.minor_grid_line_color = '#eeeeee'
fig.legend.orientation = "horizontal"
fig.legend.background_fill_color = "#fafafa"
fig.xaxis.formatter = DatetimeTickFormatter(
    months=['%b %Y'],
    days=['%b %d']
)

fig.add_tools(HoverTool(
    tooltips=[
        ('Date', '@index{%F}'),
        ('Name', '$name'),
        ('Value', '@$name')
    ],
    formatters={'@index': 'datetime'},
    ))

hugo_safe_render(fig)

Normalized stacked area chart

Generate data

norm_df = df.apply(lambda x: x/x.sum(), axis=1)
norm_df.head()

sin cos 2sin
2020-01-01 0.200000 0.400000 0.400000
2020-01-02 0.260657 0.218028 0.521315
2020-01-03 0.302499 0.092503 0.604998
2020-01-04 0.332362 0.002915 0.664723
2020-01-05 0.226031 0.321908 0.452061

pandas

norm_df.plot.area();

png

bokeh

from bokeh.plotting.figure import Figure

def bokeh_normalized_area_chart(df: pd.DataFrame) -> Figure:
    """ Use bokeh to plot a normalized stack area chart.
    
    Notes
    - Tooltips on hover
    - Expects df with DateTimeIndex and dimensions as columns

    """
    
    from bokeh.palettes import brewer
    from bokeh.plotting import figure, show
    from bokeh.io import output_notebook
    from bokeh.models import DataRange1d, HoverTool, DatetimeTickFormatter, NumeralTickFormatter, Legend

    output_notebook(hide_banner=True)

    fig = figure(
        plot_height=200,
        sizing_mode='scale_width',
        x_axis_type='datetime',
        # get rid of gap on x-axis
        x_range=DataRange1d(start=df.index.min(), end=df.index.max()),
        y_range=DataRange1d(start=0, end=1)
    )
    
    legend = Legend()
    fig.add_layout(legend, 'right')
    fig.legend.orientation = 'vertical'
    fig.legend.location = 'top_left'
    fig.legend.label_text_font_size = '8pt'
    fig.legend.background_fill_color = "#fafafa"

    names = df.columns.tolist()

    fig.varea_stack(
        stackers=names,
        x='index',
        color=brewer['Spectral'][len(names)],
        legend_label=names,
        source=df
    )

    # workaround for using HoverTool → https://stackoverflow.com/a/60895524/4866803
    fig.vline_stack(
        stackers=names,
        x='index',
        color=brewer['Spectral'][len(names)],
        legend_label=names,
        source=df
    )
    

    fig.grid.minor_grid_line_color = '#eeeeee'
    
    fig.xaxis.formatter = DatetimeTickFormatter(
        months=['%b %Y'],
        days=['%b %d']
    )
    
    fig.yaxis.formatter = NumeralTickFormatter(format='0 %')

    fig.add_tools(HoverTool(
        tooltips=[
            ('Date', '@index{%F}'),
            ('Name', '$name'),
            ('Value', '@$name{%0.2f}')
        ],
        formatters={'@index': 'datetime', '@$name' : 'printf'}
        ))

    return fig

fig = bokeh_normalized_area_chart(norm_df)
hugo_safe_render(fig)


comments powered by Disqus