Lesson 27 · Data visualisation in python
Interactive Data Filtering in Bokeh: Building Dynamic Visualizations with Python
Welcome! In this lesson, you will learn how to use Python and Bokeh to interactively filter and visualize time series data. By the end, you will be able to:…
- CourseData visualisation in python
- Lesson27 of 34
- Video14 min
- FormatJupyter notebook · 14 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbInteractive Filtering with Bokeh: A Beginner's Guide#
Welcome! In this lesson, you will learn how to use Python and Bokeh to interactively filter and visualize time series data.
By the end, you will be able to:
- Load real data with pandas.
- Create basic plots with Bokeh.
- Build interactive filters to explore trends.
- Work with a real airline passengers dataset.
Let us get started!
What is Bokeh?#
Bokeh is a Python library that makes interactive plots in the browser. You can scroll, zoom, and filter data with just a few lines of code. It is a great tool when you want to see patterns in large datasets.
import warnings
warnings.filterwarnings("ignore")
# This will keep the notebook output tidy by hiding warnings.
# Let us set up our tools.
import pandas as pd
from bokeh.plotting import figure, show, output_notebook
from bokeh.models import Slider, ColumnDataSource, CustomJS
from bokeh.layouts import column
output_notebook()
# Data setup
url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv"
df = pd.read_csv(url)
df["Month"] = pd.to_datetime(df["Month"])
df = df.sort_values("Month")
print("Shape:", df.shape)
df.head()
# Plot the passenger time series.
p = figure(title="Monthly Airline Passengers", x_axis_type="datetime", width=700, height=300)
p.line(df["Month"], df["Passengers"], line_width=2)
p.xaxis.axis_label = "Month"
p.yaxis.axis_label = "Passengers"
show(p)
Why Interactivity?#
Looking at data is powerful, but what if you want to explore? With filters, you can zoom in, focus on certain time periods, or find trends. Bokeh lets you do this with sliders and buttons. It makes discovering insights easier.
# Create a ColumnDataSource to let Bokeh update plots live.
source = ColumnDataSource(data=dict(x=df["Month"], y=df["Passengers"]))
# Make an interactive plot that can filter by year.
years = df["Month"].dt.year.unique()
slider = Slider(start=int(years.min()), end=int(years.max()), value=int(years.min()), step=1, title="Start Year")
callback = CustomJS(args=dict(source=source, slider=slider, data=df), code="""
var data = source.data;
var all_data = data;
var df_data = data;
var year = slider.value;
var x = [];
var y = [];
for (var i = 0; i < data['x'].length; i++) {
var thisYear = new Date(data['x'][i]).getFullYear();
if (thisYear >= year) {
x.push(data['x'][i]);
y.push(data['y'][i]);
}
}
source.data = {x: x, y: y};
source.change.emit();
""")
slider.js_on_change('value', callback)
p2 = figure(title="Passengers (Move slider to filter)", x_axis_type="datetime", width=700, height=300)
p2.line('x', 'y', source=source, line_width=2, color="green")
p2.xaxis.axis_label = "Month"
p2.yaxis.axis_label = "Passengers"
show(column(slider, p2))
# Add a second slider to set an upper year limit.
from bokeh.models import RangeSlider
range_slider = RangeSlider(start=int(years.min()), end=int(years.max()), value=(int(years.min()), int(years.max())), step=1, title="Year Range")
callback2 = CustomJS(args=dict(source=source, slider=range_slider, fullx=df["Month"].tolist(), fully=df["Passengers"].tolist()), code="""
var yr0 = slider.value[0];
var yr1 = slider.value[1];
var x = [];
var y = [];
for (var i = 0; i < fullx.length; i++) {
var dt = new Date(fullx[i]);
var yv = dt.getFullYear();
if (yv >= yr0 && yv <= yr1) {
x.push(fullx[i]);
y.push(fully[i]);
}
}
source.data = {x: x, y: y};
source.change.emit();
""")
range_slider.js_on_change('value', callback2)
p3 = figure(title="Passengers (Filter By Year Range)", x_axis_type="datetime", width=700, height=300)
p3.line('x', 'y', source=source, line_width=2, color="navy")
p3.xaxis.axis_label = "Month"
p3.yaxis.axis_label = "Passengers"
show(column(range_slider, p3))
Break Time: Recap!#
So far, you have learned how to:
- Set up Bokeh and pandas.
- Visualize real data.
- Use interactive sliders to filter time ranges.
It is starting to look powerful, right?
# Mini exercise: How many months in your current filter?
n_months = len(source.data['x'])
print("There are", n_months, "months shown in the filtered plot.")
# Filtering data by value: Only show months with more than 400 passengers.
from bokeh.models import CheckboxGroup, CustomJSFilter, CDSView
over_400 = CheckboxGroup(labels=["Show only months with > 400 passengers"], active=[])
callback3 = CustomJS(args=dict(source=source, cb=over_400, fullx=df["Month"].tolist(), fully=df["Passengers"].tolist()), code="""
var show_high = cb.active.length > 0;
var x = [];
var y = [];
for (var i = 0; i < fullx.length; i++) {
if (!show_high || fully[i] > 400) {
x.push(fullx[i]);
y.push(fully[i]);
}
}
source.data = {x: x, y: y};
source.change.emit();
""")
over_400.js_on_change("active", callback3)
p4 = figure(title="Passengers (Above 400 Option)", x_axis_type="datetime", width=700, height=300)
p4.line('x', 'y', source=source, line_width=2, color="firebrick")
p4.xaxis.axis_label = "Month"
p4.yaxis.axis_label = "Passengers"
show(column(over_400, p4))
# Try your own filter: enter a passenger threshold!
thresh = input("Show only months above how many passengers?")
try:
threshold = int(thresh)
except:
print("Please enter a valid number.")
threshold = 300
mask = df["Passengers"] > threshold
filtered = df[mask]
print("Number of months above threshold:", filtered.shape[0])
# (Mini-project) Build a complete dashboard for filtering.
from bokeh.models import TextInput, Toggle
passenger_input = TextInput(value="300", title="Passenger Threshold (> this many):")
toggle = Toggle(label="Only show busy months", button_type="success", active=False)
callback4 = CustomJS(args=dict(source=source, input=passenger_input, toggle=toggle, fullx=df["Month"].tolist(), fully=df["Passengers"].tolist()), code="""
var threshold = parseInt(input.value) || 0;
var busy = toggle.active;
var x = [];
var y = [];
for (var i = 0; i < fully.length; i++) {
if (!busy || fully[i] > threshold) {
x.push(fullx[i]);
y.push(fully[i]);
}
}
source.data = {x: x, y: y};
source.change.emit();
""")
passenger_input.js_on_change("value", callback4)
toggle.js_on_change("active", callback4)
p5 = figure(title="Passenger Custom Dashboard", x_axis_type="datetime", width=700, height=300)
p5.line('x', 'y', source=source, line_width=3, color="orange")
p5.xaxis.axis_label = "Month"
p5.yaxis.axis_label = "Passengers"
show(column(passenger_input, toggle, p5))
# Best practices: Keeping data and visualization separate.
def filtered_df(dataframe, min_passengers=0):
return dataframe[dataframe["Passengers"] > min_passengers]
print("Example: months with over 350 passengers:")
display(filtered_df(df, 350).head())
# Troubleshooting: What if your Bokeh widgets do not update?
# Common reasons: the source is not being changed, or JS errors stop the callback.
# Try printing or logging inside your callback to test.
# Also: make sure you use ColumnDataSource and not plain DataFrames.
print("If your plot is stuck, double-check your source setup and widget wiring.")
Extra tip: You can export your interactive plots!#
Bokeh lets you save your dashboards as HTML files for sharing. Just use bokeh.io.output_file("mydashboard.html") before show(...). It works great for presentations at work or school.
# Challenge: Try making a filter for the busiest quarter.
quarters = df["Month"].dt.quarter
max_q = quarters[df["Passengers"].idxmax()]
print("The busiest quarter in the data is Q", max_q, ".")
Recap: What have you learned?#
You loaded time series data, built live plots, and created rich filters. Bokeh makes interactive exploration simple even for beginners. These skills work with many business and science datasets. Nice work reaching this point!
Thanks for learning with us!#
Try out your new Bokeh skills on your own data. Share your dashboards with friends, classmates, or your boss. Subscribe for more beginner tutorials and tips right here on YouTube!
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



