Creating Interactive Bokeh Plots with Selectable Columns: A Step-by-Step Guide

Bokeh Plot with Selectable Columns

Introduction

Bokeh is an interactive visualization library that allows users to create web-based interactive plots and dashboards. In this article, we will explore how to use Bokeh to create a plot where the user can select different columns from a pandas DataFrame.

We will also cover the concepts of ColumnDataSource, CustomJS, and Select in Bokeh. These are essential components for creating dynamic and interactive visualizations with Bokeh.

Setting Up the Environment

To start working with Bokeh, we need to install it using pip:

pip install bokeh pandas matplotlib numpy

We also need to have pandas, matplotlib, and numpy installed in our environment.

Creating the DataFrame

First, let’s create a sample DataFrame with several columns and a numeric index.

import pandas as pd

df = pd.DataFrame({'2007':[10,20,30,40], 
                   '2008':[90,60,70,40], 
                   '2009':[30,60,70,10], 
                   '2010':[80,50,30,10]},
                  index=[0, 0.5, 1, 1.5])

Creating the Plot

Next, we create a figure with a linear x-axis and add a line renderer for one of the columns.

from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.io import show
from bokeh.models.tools import HoverTool
from bokeh.models import Select
from bokeh.layouts import column

ds = ColumnDataSource(df)
p = figure(toolbar_location="above", 
           x_axis_type="linear") 

p.line(source=ds, y='index', x='2007')

Creating the Select Widget

We create a Select widget with options that correspond to the columns in our DataFrame.

select = Select(title="df-column:", options=list(df.columns))

Connecting the Select Widget to the CustomJS Function

We connect the Select widget’s ‘value’ event to a CustomJS function that updates the glyph.x value of the line renderer.

handler = CustomJS(args=dict(source=ds, line_renderer=p.line), code="""
   // Update glyph.x based on selected column
""")

select.js_on_change('value', handler)

Displaying the Plot

Finally, we display the plot with the Select widget.

show(column(select, p))

Understanding the Code

Let’s break down the CustomJS function:

handler = CustomJS(args=dict(source=ds, line_renderer=p.line), code="""
   line_renderer.glyph.x = {field: cb_obj.value};
""")

In this function:

  • source: refers to the ColumnDataSource object created earlier.
  • line_renderer: refers to the p.line method that creates a line renderer.
  • cb_obj.value: refers to the value selected by the user in the Select widget.

The CustomJS function updates the glyph.x value of the line renderer based on the selected column. This allows the user to change the x-axis values for different columns.

Troubleshooting

One potential issue with this code is that it assumes the user will always select a valid column from the DataFrame. If the user selects an invalid column, the plot may not update correctly. To fix this, we can add some error handling to ensure that only valid columns are selected:

handler = CustomJS(args=dict(source=ds, line_renderer=p.line), code="""
   if (cb_obj.value in df.columns) {
     line_renderer.glyph.x = {field: cb_obj.value};
   } else {
     console.error("Invalid column selected");
   }
""")

Conclusion

In this article, we explored how to use Bokeh to create a plot where the user can select different columns from a pandas DataFrame. We covered the concepts of ColumnDataSource, CustomJS, and Select in Bokeh, and provided example code to demonstrate how to connect these components together.

By following these steps and using the custom JavaScript function, you can create dynamic and interactive visualizations with Bokeh that allow users to explore your data in new ways.

Advanced Topics

Using Multiple Lines Renderers

To add multiple lines renderers to the plot, we can use a list comprehension to create a list of line renderer objects:

line_renderers = [p.line('2007', 'index', source=ds) for column in df.columns]

We can then update each line renderer using a separate CustomJS function:

handler1 = CustomJS(args=dict(source=ds, line_renderer=line_renderers[0]), code="""
   line_renderer.glyph.x = {field: cb_obj.value};
""")

handler2 = CustomJS(args=dict(source=ds, line_renderer=line_renderers[1]), code="""
   line_renderer.glyph.x = {field: cb_obj.value};
""")

select.js_on_change('value', [handler1, handler2])

This allows the user to select different columns and view their corresponding lines in the plot.

Using Hover Tool

To add a hover tool to the plot, we can use the HoverTool class:

hover = HoverTool(tooltips=[("x", "@x"), ("y", "@y")])

We can then add this hover tool to the plot using the add_tools method:

p.add_tools(hover)

This allows the user to view the x and y values of each data point when they hover over it in the plot.


Last modified on 2023-09-11