import pandas as pd
# Our main plotting package (must have explicit import of submodules)
import bokeh.io
import bokeh.models
import bokeh.plotting
import bokeh.transform
# Enable viewing Bokeh plots in the notebook
bokeh.io.output_notebook()
15 Interactive Data Visualization with Bokeh
In the previous two lessons, you learned how to visualize data using pandas’ high-level plotting tools for quick insights, and matplotlib for more detailed and customized charting.
In this lesson, you’ll take your visualization skills to the next level with Bokeh, a Python library designed for building interactive visualizations in modern web browsers. With Bokeh, you can create everything from simple scatterplots to fully-featured, dynamic dashboards — all in Python, no JavaScript required.
Tools like Bokeh allow you to move beyond static charts and into the world of interactive, responsive data experiences. This shift enables visual storytelling, where users can explore data themselves, and helps you deliver more engaging, stakeholder-friendly output. Whether you’re sharing insights with a non-technical audience or designing a data dashboard, Bokeh gives you the flexibility and power to communicate results in more impactful ways.
Although Bokeh is considered a lower-level visualization API than pandas or seaborn, it strikes a great balance between ease of use and customizability. You’ll find it intuitive to get started with and incredibly powerful as you go deeper.
In this chapter, you’ll get hands-on experience with Bokeh and learn how to create and customize interactive visualizations for the web. By the end of this lesson you will be able to:
- Use Bokeh’s figure interface to build interactive charts from scratch.
- Add hover tools, legends, and other interactive components to your plots.
As you read through this chapter, we encourage you to follow along using the companion notebook in Google Colab (or other editor of choice). This interactive notebook lets you run code examples covered in the chapter—and experiment with your own ideas.
👉 Open the Bokeh Data Viz Notebook in Colab.
15.1 Prerequisites
Most of the functionality of Bokeh is accessed through submodules such as bokeh.plotting
and bokeh.models
. Also, when using Bokeh in a notebook we need to run bokeh.io.output_notebook()
to make our plots viewable and interactive.
We’ll use a cleaned up version of the Ames, IA housing data for illustration purposes:
= pd.read_csv('../data/ames_clean.csv')
df df.head()
Id | MSSubClass | MSZoning | LotFrontage | LotArea | Street | Alley | LotShape | LandContour | Utilities | ... | PoolArea | PoolQC | Fence | MiscFeature | MiscVal | MoSold | YrSold | SaleType | SaleCondition | SalePrice | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 60 | RL | 65.0 | 8450 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2008 | WD | Normal | 208500 |
1 | 2 | 20 | RL | 80.0 | 9600 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 5 | 2007 | WD | Normal | 181500 |
2 | 3 | 60 | RL | 68.0 | 11250 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 9 | 2008 | WD | Normal | 223500 |
3 | 4 | 70 | RL | 60.0 | 9550 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2006 | WD | Abnorml | 140000 |
4 | 5 | 60 | RL | 84.0 | 14260 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 12 | 2008 | WD | Normal | 250000 |
5 rows × 81 columns
15.2 Bokeh’s grammar and our first plot with Bokeh
Constructing a plot with Bokeh consists of four main steps.
- Creating a figure on which to populate glyphs (symbols that represent data, e.g., dots for a scatter plot). Think of this figure as a “canvas” which sets the space on which you will “paint” your glyphs.
- Defining a data source that is the reference used to place the glyphs.
- Choose the kind of glyph you would like.
- Refining the plot by adding titles, formatted axis labels, or even interactive components.
After completing these steps, you need to render the graphic.
Let’s go through these steps to generate an interactive scatter plot of home sales price and total living area. So you have the concrete example in mind, the final graphic will look like this:
1. Our first step is creating a figure, our “canvas.” In creating the figure, we are implicitly thinking about what kind of representation for our data we want. That is, we have to specify axes and their labels. We might also want to specify the title of the figure, whether or not to have grid lines, and all sorts of other customizations. Naturally, we also want to specify the size of the figure.
(Almost) all of this is accomplished in Bokeh by making a call to bokeh.plotting.figure()
with the appropriate keyword arguments.
# Create the figure, stored in variable `p`
= bokeh.plotting.figure(
p =700,
frame_width=350,
frame_height='Relationship between home sale price and living area \nAmes, Iowa (2006-2010)',
title='Living Area (Square feet)',
x_axis_label='Sale Price'
y_axis_label )
There are many more keyword attributes you can assign, including all of those listed in the Bokeh Plot class and the additional ones listed in the Bokeh Figure class.
2. Now that we have set up our canvas, we can decide on the data source. It is convenient to create a ColumnDataSource
, a special Bokeh object that holds data to be displayed in a plot. (Later on we will see that we can change the data in a ColumnDataSource
and the plot will automatically update!) Conveniently, we can instantiate a ColumnDataSource
directly from a Pandas data frame.
= bokeh.models.ColumnDataSource(df) source
We could also instantiate a data source using a dictionary of arrays, like…
source = bokeh.models.ColumnDataSource(dict(x=[1, 2, 3, 4], y=[1, 4, 9, 16]))
3. Since we are creating a scatter plot we will choose scatter as our glyph. This kind of glyph requires that we specify which column of the data source will serve to place the glyphs along the \(x\)-axis and which will serve to place the glyphs along the \(y\)-axis. We choose the 'GrLivArea'
column to specify the \(x\)-coordinate of the glyph and the 'SalePrice'
column to specify the \(y\)-coordinate. Since there are a lot of observations clustered together we can control overplotting by adjusting the transparency with alpha
.
We accomplish step 3 by calling one of the glyph methods of the Bokeh Figure
instance, p
. Since we are choosing a scatter plot, the appropriate method is p.scatter()
, and we use the source
, x
, and y
kwargs to specify the positions of the glyphs.
p.scatter(=source,
source='GrLivArea',
x='SalePrice',
y=0.25
alpha; )
4. Lastly, we can refine the plot in various ways. In this example we make the x and y-axis labels comma and dollar formatted respectively. We can also add interactive components to our visuals. Here, I add a hover tool so that sale price and total living area is displayed when my mouse hovers over a point.
We can specify these features (axis configuration and tooltips) when we instantiate the figure or afterwards by assigning attribute values to an already instantiated figure.
The syntax for a tooltip is a list of 2-tuples, where each tuple represents the tooltip you want. The first entry in the tuple is the label and the second is the column from the data source that has the values. The second entry must be preceded with an @
symbol signifying that it is a field in the data source and not field that is intrinsic to the plot, which is preceded with a $
sign. If there are spaces in the column heading, enclose the column name in braces (i.e. {name with spaces}
). (See the documentation for tooltip specification for more information.)
= bokeh.models.NumeralTickFormatter(format="$,")
p.yaxis.formatter = bokeh.models.NumeralTickFormatter(format=",")
p.xaxis.formatter
= [("Sale Price","@SalePrice"),("SqFt","@GrLivArea")]
tooltips = bokeh.models.HoverTool(tooltips=tooltips, mode='mouse')
hover p.add_tools(hover)
Now that we have built the plot, we can render it in the notebook using bokeh.io.show()
.
bokeh.io.show(p)
In looking at the plot, notice a toolbar to right of the plot that enables you to zoom and pan within the plot.
15.3 Coloring with other dimensions
Let’s say we wanted to make the same plot, but we wanted to color the points based on another feature such as whether the home has central air or not (CentralAir
). To do this, we take advantage of two features of Bokeh.
- We create a color mapping using
factor_cmap()
that assigns colors to the discrete levels of a given factor (CentralAir in this example). Here, we simply assign red and blue colors; however, Bokeh has many color palettes to choose from. - We can then use the
scatter
method to assign the glyph of choice and pass thecolor_mapper
object tofill_color
and/orfill_line
. I also add the legend field so it shows up in the plot and we can format our legend as necessary (i.e. add title, change font).
# Create the figure, stored in variable `p`
= bokeh.plotting.figure(
p =700,
frame_width=350,
frame_height='Relationship between home sale price and living area \nAmes, Iowa (2006-2010)',
title='Living Area (Square feet)',
x_axis_label='Sale Price'
y_axis_label
)
= bokeh.models.ColumnDataSource(df)
source
# create color mapper
= bokeh.transform.factor_cmap(
color_mapper 'CentralAir',
=['red', 'blue'],
palette=df['CentralAir'].unique()
factors
)
p.scatter(=source,
source='GrLivArea',
x='SalePrice',
y='circle',
marker=0.25,
alpha=color_mapper,
fill_color=color_mapper,
line_color='CentralAir'
legend_field
)
= "Has central air"
p.legend.title
= bokeh.models.NumeralTickFormatter(format="$,")
p.yaxis.formatter = bokeh.models.NumeralTickFormatter(format=",")
p.xaxis.formatter
= [("Sale Price","@SalePrice"),("SqFt","@GrLivArea")]
tooltips = bokeh.models.HoverTool(tooltips=tooltips, mode='mouse')
hover
p.add_tools(hover)
bokeh.io.show(p)
15.4 Saving Bokeh plots
After you create your plot, you can save it to a variety of formats. Most commonly you would save them as PNG (for presentations), SVG (for publications in the paper of the past), and HTML (for the paper of the future or sharing with colleagues).
To save as a PNG for quick use, you can click the disk icon in the tool bar.
To save to SVG, you first change the output backend to 'svg'
and then you can click the disk icon again, and you will get an SVG rendering of the plot. After saving the SVG, you should change the output backend back to 'canvas'
because it has much better in-browser performance.
= 'svg'
p.output_backend
bokeh.io.show(p)
Now, click the disk icon in the plot above to save it.
After saving, we should switch back to canvas.
= 'canvas' p.output_backend
You can also save the figure programmatically using the bokeh.io.export_svgs()
function. This requires additional installations, so we will not do it here, but show the code to do it. Again, this will only work if the output backed is 'svg'
.
= 'svg'
p.output_backend ='ames_sale_price_vs_living_area.svg')
bokeh.io.export_svgs(p, filename= 'canvas' p.output_backend
Finally, to save as HTML, you can use the bokeh.io.save()
function. This saves your plot as a standalone HTML page. Note that the title
kwarg is not the title of the plot, but the title of the web page that will appear on your Browser tab.
bokeh.io.save(
p,='ames_sale_price_vs_living_area.html',
filename='Bokeh plot'
title; )
15.5 Video Tutorial
This lesson only scratches the surface of what Bokeh can do. From interactive widgets and streaming data to fully responsive dashboards, Bokeh offers a wide range of advanced capabilities. To see more examples and explore what’s possible, visit the official Bokeh gallery at https://demo.bokeh.org/.
15.6 Summary
In this chapter, you explored the fundamentals of creating interactive data visualizations using Bokeh, a powerful Python library designed for the web. You learned how to use Bokeh’s figure
interface to build plots from scratch, enhance them with interactive tools like hovers and legends, and combine multiple charts into dashboard-style layouts.
Bokeh opens the door to interactive, engaging visual storytelling—ideal for exploratory analysis and sharing insights with stakeholders in a more dynamic format. While this chapter focused on the core building blocks, Bokeh supports much more, including advanced widgets, real-time data streaming, and fully customizable layouts.
To explore Bokeh’s full capabilities, check out the official demo gallery.
15.7 Exercise: Exploring Housing Trends with Bokeh
In this exercise set, you’ll revisit the Ames Housing dataset and apply what you’ve learned about Bokeh to build interactive plots from scratch. These visualizations will give you hands-on practice with building charts, customizing tooltips, applying categorical coloring, and creating simple layouts.
Use the Ames Housing dataset we used in this chapter, and import the necessary Bokeh modules as needed:
import pandas as pd
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.transform import factor_cmap
from bokeh.layouts import row, column
from bokeh.models import HoverTool
output_notebook()
= pd.read_csv("data/ames_clean.csv") # adjust path as needed df
Use tools like ChatGPT, GitHub Copilot, or the Bokeh documentation to help troubleshoot, format your plots, or discover ways to customize interactivity. Learning how to adapt examples is part of becoming a confident data visualizer.