rain123
rain123

Reputation: 273

Plotting Multiple Plots of Bar Charts in Bokeh Using Looping

I have a dataframe with multiple columns as below:

data = pd.DataFrame({'Year': ['2016', '2017', '2018', '2019', '2020'],
                 'A1 Qty': [743.85, 608.75, 1099.14, 1253.50, 239.45],
                 'A2 Qty': [0.0, 0.0, 0.0, 1280.78, 1138.66],
                 'B1 Qty': [153.3, 213.04, 125.85, 0.0, 0.0],
                 'B2 Qty': [832.93, 1080.74, 1188.46, 0.0, 0.0],
                 'C1 Qty': [11.47, 9.52, 11.57, 10.1, 1.52],
                 'C2 Qty': [14.33, 15.88, 2.53, 9.98, 1.87]
                })

I want to plot multiple bar graphs in different plots and i managed to do it one by one using the code below:

src = ColumnDataSource(data)
y_max = max(src.data['A1 Qty'].max(), src.data['A2 Qty'].max())
y_max*=1.50

y_max1 =  max(src.data['B1 Qty'].max(), src.data['B2 Qty'].max())
y_max1*=1.50

y_max2 =  max(src.data['C1 Qty'].max(), src.data['C2 Qty'].max())
y_max2*=1.50

#if loop what change? y_range, title,

s = figure(x_range=src.data['Year'], y_range=(0, y_max), plot_height=250, title="A1 vs A2 by Year",
           toolbar_location=None, tools="")

s.vbar(x=dodge('Year', -0.25, range=s.x_range), top='A1 Qty', width=0.2, source=src,
       color="gold", legend="Actual")#, legend_label="Actual")

s.vbar(x=dodge('Year',  0, range=s.x_range), top='A2 Qty', width=0.2, source=src,
       color="indianred", legend="Recommended")#,  legend_label="Recommended")

s1 = figure(x_range=src.data['Year'], y_range=(0, y_max1), plot_height=250, title="B1 vs B2 by Year",
           toolbar_location=None, tools="")

s1.vbar(x=dodge('Year', -0.25, range=s1.x_range), top='B1 Qty', width=0.2, source=src,
       color="gold", legend="Actual")#, legend_label="Actual")

s1.vbar(x=dodge('Year',  0, range=s1.x_range), top='B2 Qty', width=0.2, source=src,
       color="indianred", legend="Recommended")#,  legend_label="Recommended")


s2 = figure(x_range=src.data['Year'], y_range=(0, y_max2), plot_height=250, title="C1 vs C2 by Year",
           toolbar_location=None, tools="")

s2.vbar(x=dodge('Year', -0.25, range=s2.x_range), top='C1 Qty', width=0.2, source=src,
       color="gold", legend="Actual")#, legend_label="Actual")

s2.vbar(x=dodge('Year',  0, range=s2.x_range), top='C2 Qty', width=0.2, source=src,
       color="indianred", legend="Recommended")#,  legend_label="Recommended")


layout = row(s,s1,s2)

show(layout)

As you can see, this is very ineffective way because i need to repetitively define y_max, figure, and vbar. How do i do this using loop because I have more than 20 columns to plot.

Upvotes: 0

Views: 416

Answers (1)

Eugene Pakhomov
Eugene Pakhomov

Reputation: 10652

You don't need to set the ranges manually - just specify the start value and the span. Bokeh will compute the end value for you.

import pandas as pd

from bokeh.io import show
from bokeh.layouts import row
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.transform import dodge

data = pd.DataFrame({'Year': ['2016', '2017', '2018', '2019', '2020'],
                     'A1 Qty': [743.85, 608.75, 1099.14, 1253.50, 239.45],
                     'A2 Qty': [0.0, 0.0, 0.0, 1280.78, 1138.66],
                     'B1 Qty': [153.3, 213.04, 125.85, 0.0, 0.0],
                     'B2 Qty': [832.93, 1080.74, 1188.46, 0.0, 0.0],
                     'C1 Qty': [11.47, 9.52, 11.57, 10.1, 1.52],
                     'C2 Qty': [14.33, 15.88, 2.53, 9.98, 1.87]})

src = ColumnDataSource(data)


def mk_plot(label1, label2):
    s = figure(x_range=src.data['Year'], plot_height=250, title=f"{label1} vs {label2} by Year",
               toolbar_location=None, tools="")
    s.y_range.start = 0
    # 1 instead of 0.5 because it gets divided by half, but the "start" half
    # is not taken into account because we set the start manually.
    s.y_range.range_padding = 1

    s.vbar(x=dodge('Year', -0.25, range=s.x_range), top=f'{label1} Qty', width=0.2, source=src,
           color="gold", legend_label="Actual")
    s.vbar(x=dodge('Year', 0, range=s.x_range), top=f'{label2} Qty', width=0.2, source=src,
           color="indianred", legend_label="Recommended")
    return s


show(row([mk_plot(f'{l}1', f'{l}2') for l in 'ABC']))

Upvotes: 2

Related Questions