Interactive Bar Charts with Bokeh
By: Ilya Kvyatkovskiy
Tags: Data Visualization, Bar Chart, Bokeh, Data Science, Interactive

Styling Bar Charts

For a list of some available palettes please visit Bokeh palettes documentation. In order to use any of them with Bokeh we need to import them specifically.

Let’s look at the measles data for a number of countries in 2015 — we’ll render two graphs with a pre-set palette and a randomly chosen colors, as well as we will use the *gridplot* technique.

Palettes and Gridplot

#Importing a pallette from bokeh.palettes import Spectral5, Viridis256, Colorblind, Magma256, Turbo256 # Creating a list of categorical values values = data[(data['year']==2015)&(data['country'].isin(countries))]['measles'] # Set the x_range to the list of categories above p1 = figure(x_range=countries,

plot_height=250,

title="Measles in the world in 2015 (pre-set pallette)") # Categorical values can also be used as coordinates p1.vbar(x=countries, top=values, width=0.9,

color = Spectral5, fill_alpha=.75) # Set some properties to make the plot look better p1.yaxis.axis_label="Measles stats"

p1.xgrid.grid_line_color='gray'

p1.xgrid.grid_line_alpha=.75

p1.xgrid.grid_line_dash = 'dashed'

p1.ygrid.grid_line_color='blue'

p1.ygrid.grid_line_alpha = .55

p1.ygrid.grid_line_dash = 'dotted' p2 = figure(x_range=countries,

plot_height=250,

title="Measles in the world in 2015 (randomly selected colors from a pallette)") # Categorical values can also be used as coordinates p2.vbar(x=countries, top=values, width=0.9,

color = random.sample(Viridis256,5), fill_alpha=.75) # Set some properties to make the plot look better p2.yaxis.axis_label="Measles stats"

p2.xgrid.grid_line_color='gray'

p2.xgrid.grid_line_alpha=.75

p2.xgrid.grid_line_dash = 'dashed'

p2.ygrid.grid_line_color='blue'

p2.ygrid.grid_line_alpha = .55

p2.ygrid.grid_line_dash = 'dotted' p = gridplot([[p1,None],[p2,None]], toolbar_location='right')

show(p)

And here’s the result:

This plot looks much friendlier then the ones we started with. And there’s no end to experiments with colors and palettes.

Grouped Bar Charts

Sometimes we need to plot a grouped bar chart. For example, we might need to group our health indicators for some countries. For that, we need to import a special procedure from the bokeh.models module — FactorRange. Let’s look at the data for measles, polio and hiv/aids*1000 for our list of countries for 2014.

from bokeh.models import FactorRange #List of used statistics stats = ['measles','polio','hiv/aids*1000'] #Creating a dictionary of our data mdata = {'countries' : countries,

'measles' : data[data['year']==2014][data['country'].isin(countries)]['measles'],

'polio' : data[data['year']==2014][data['country'].isin(countries)]['polio'],

'hiv/aids*1000' : data[data['year']==2014][data['country'].isin(countries)]['hiv/aids']*1000} # Creating tuples for individual bars [ ("France", "measles"), ("France", "polio"), ("France", "hiv/aids*1000"), ("Canada", "measles"), ... ] x = [ (country, stat) for country in countries for stat in stats ]

counts = sum(zip(mdata['measles'], mdata['polio'], mdata['hiv/aids*1000']), ()) #Creating a column data source - Bokeh's own data type with the fields (Country,[stats],[values],[colors]) source = ColumnDataSource(data=dict(x=x, counts=counts, color=random.sample(Turbo256,15))) #Initializing our plot p = figure(x_range=FactorRange(*x), plot_height=350, title="Health Stats by Country") #Plotting our vertical bar chart p.vbar(x='x', top='counts', width=0.9 ,fill_color='color', source=source) #Enhancing our graph p.y_range.start = 0

p.x_range.range_padding = 0.1

p.xaxis.major_label_orientation = .9

p.xgrid.grid_line_color = None show(p)

And here’s the plot:

We could use a zillion of possible in-built methods to adjust the visual to our liking as well.

Color Transformations

Quite often we are not satisfied with a pre-set or a random palette, and we need to use some additional colormapping. That’s the situation when we use factor_cmap function imported from bokeh.transform module. Let’s look at the Canadian data for measles, polio and hiv/aids*1000 in 2000, 2005, 2010 and 2015 respectively.

from bokeh.transform import factor_cmap #List of used statistics stats = ['measles','polio','hiv/aids*1000']

years = ['2000','2005','2010','2015'] #Creating a dictionary of our data mdata = {'years' : years,

'measles' : data[data['country']=="Canada"][data['year'].isin(years)]['measles'],

'polio' : data[data['country']=="Canada"][data['year'].isin(years)]['polio'],

'hiv/aids*1000' : data[data['country']=="Canada"][data['year'].isin(years)]['hiv/aids']*1000} # Creating tuples for individual bars x = [ (year, stat) for year in years for stat in stats ]

counts = sum(zip(mdata['measles'], mdata['polio'], mdata['hiv/aids*1000']), ()) #Creating a column data source source = ColumnDataSource(data=dict(x=x, counts=counts, color=random.sample(Turbo256,12))) #Initializing our plot with random colors p1 = figure(x_range=FactorRange(*x), plot_height=350, title="Health Stats in Canada 2000-2015") #Plotting our vertical bar chart p1.vbar(x='x', top='counts', width=0.9 ,fill_color='color', source=source) #Enhancing our graph p1.y_range.start = 0

p1.x_range.range_padding = 0.1

p1.xaxis.major_label_orientation = .9

p1.xgrid.grid_line_color = None #Creating a new column data source without set colors



source1 = ColumnDataSource(data=dict(x=x, counts=counts)) #Initializing our plot with synchronized fill colors with factor_cmap p2 = figure(x_range=FactorRange(*x), plot_height=350,

title="Health Stats in Canada 2000-2015, color mapped"

) p2.vbar(x='x', top='counts', width=0.9,

source=source1,

fill_color=factor_cmap('x', palette=['salmon', 'green', 'navy'],

factors=stats, start=1, end=2) ) p2.xaxis.major_label_orientation = .7

p=gridplot([[p1,None],[p2,None]], toolbar_location='right') show(p)

And here we are — the first chart has some random colors, the second one is color factored:

Even though the first one looks funkier, the second one has a much more clear message when a color mapping statistics.