5 Best Ways to Utilize Bokeh Library to Visualize Twin Axes in Python

πŸ’‘ Problem Formulation: When working with data visualization in Python, it’s common to encounter scenarios where you want to compare two different datasets with different scales on the same plot. Twin axes are useful for this purpose, allowing for a clear and intuitive comparison. For example, you might want to present temperature and humidity data over time, where temperature is on the left y-axis and humidity is on the right y-axis. Bokeh, as a powerful interactive visualization library, can handle this elegantly. Here, we discuss different methods to create such visualizations.

Method 1: Creating a Secondary Axis Using Bokeh’s LinearAxis

Bokeh’s LinearAxis allows the inclusion of a secondary axis on a plot, which is ideal for comparing two scales. By aligning a new axis with a different range or scale, we can present two datasets concurrently in an insightful manner.

Here’s an example:

from bokeh.plotting import figure, show
from bokeh.models import LinearAxis, Range1d

# create a new plot with a range for the primary axis
p = figure(x_axis_type="datetime", y_range=(0, 100))

# adding a second axis with a different range
p.extra_y_ranges = {"foo": Range1d(start=0, end=200)}
p.add_layout(LinearAxis(y_range_name="foo"), 'right')

# show the results
show(p)

The output would be a Bokeh plot with two vertical axes, one on the left and the other on the right.

In this code snippet, we create a Bokeh figure with a datetime x-axis and a primary y-axis ranging from 0 to 100. We then introduce an additional y-axis named “foo” with a range from 0 to 200 and position it on the right by using the add_layout() function. We end by displaying the plot, which now features twin axes.

Method 2: Synchronizing Twin Axes with Different Scales

Bokeh makes it possible to have twin axes that operate on different scales but are visually synchronized, meaning that they move and zoom together. This is especially useful for overlaying different types of charts that share a common axis.

Here’s an example:

from bokeh.plotting import figure, show
from bokeh.models import LinearAxis, Range1d

p = figure(y_range=(0, 100))

# adding a second axis with a different range and synchronizing it
p.extra_y_ranges = {"temp": Range1d(start=0, end=200)}
p.add_layout(LinearAxis(y_range_name="temp"), 'right')
p.y_range.on_change('start', lambda attr, old, new: setattr(p.extra_y_ranges["temp"], 'start', new*2))
p.y_range.on_change('end', lambda attr, old, new: setattr(p.extra_y_ranges["temp"], 'end', new*2))

show(p)

The output is a plot with axes that are synchronized during zooming or panning.

This snippet extends Method 1 by adding callbacks to the primary y-axis range changes. When the primary axis is zoomed or panned, the code synchronizes the secondary axis accordingly by doubling the start and end values. This ensures that while the scales are different, the axes move simultaneously and remain aligned, enhancing the comparison power of the visualization.

Method 3: Overlaying Different Types of Glyphs

Bokeh supports the overlay of different glyphs on the same plot, each associated with its axis. Combining, for example, a line glyph for one dataset and a bar glyph for another dataset enhances contrast and comparability.

Here’s an example:

from bokeh.plotting import figure, show
from bokeh.models import LinearAxis, Range1d

p = figure(x_axis_type="datetime", y_range=(0, 100))

# adding bar glyphs
p.vbar(x=dates, width=0.7, bottom=0, top=humidity, color="blue")

# adding a secondary axis and line glyph
p.extra_y_ranges = {"temp": Range1d(start=0, end=200)}
p.add_layout(LinearAxis(y_range_name="temp"), 'right')
p.line(dates, temperature, color="red", y_range_name="temp")

show(p)

The output is a Bokeh plot with bars representing one dataset and a line representing another dataset, each tied to its axis.

In this code, we first add blue vertical bars representing the humidity dataset. We then create a secondary y-axis for temperature and plot it as a red line. By specifying the y_range_name for each glyph, we tie the bars and the line to their respective axes, allowing for a clear and distinct visual comparison of the two datasets.

Method 4: Customizing Twin Axes for Enhanced Readability

For readability, we can customize the appearance of each axis separately in Bokeh. Modifications could include axis color, axis label, and tick formatting, which help to distinguish the different axes and datasets.

Here’s an example:

from bokeh.plotting import figure, show
from bokeh.models import LinearAxis, Range1d

p = figure(y_range=(0, 100))

# setting up the primary axis
p.yaxis.axis_label = "Primary Axis Label"
p.yaxis.axis_line_color = "green"

# adding and customizing a second axis
p.extra_y_ranges = {"secondary": Range1d(start=0, end=200)}
secondary_axis = LinearAxis(y_range_name="secondary", axis_label="Secondary Axis Label")
secondary_axis.axis_line_color = "red"
p.add_layout(secondary_axis, 'right')

show(p)

The visualization will depict two distinctly styled y-axes to improve the interpretability of the data.

This code snippet emphasizes the customization of both the primary and secondary axes. The primary axis is labeled and colored green, while the secondary axis, added through a similar process as before, is labeled distinctly and colored red. Through these customizations, the twin axes are easily distinguishable, aiding in the visual separation of the dual datasets.

Bonus One-Liner Method 5: Adding a Simple Secondary Axis

For quick and simple twin axes, Bokeh allows for a one-liner addition of a secondary axis with a shared range but labeled differently for quick differentiation.

Here’s an example:

p = figure(y_range=(0, 100))
p.extra_y_ranges = {"foo": p.y_range}
p.add_layout(LinearAxis(y_range_name="foo", axis_label="Secondary"), 'right')
show(p)

The result is a plot with twin y-axes having the same range but different labels.

We see a straightforward method here: we define an additional y-axis “foo” that shares the same range as the primary axis but give it a different label “Secondary”. This is a rapid way to create twin axes when the datasets are comparable on the same scale but require distinct labels or color coding.

Summary/Discussion

  • Method 1: Creating a Secondary Axis Using Bokeh’s LinearAxis. This method is straightforward and easy to implement, great for comparing datasets with different scales. However, it doesn’t ensure visual synchronization during interactive zooming or panning.
  • Method 2: Synchronizing Twin Axes with Different Scales. This technique guarantees that the twin axes are visually synchronized during interactions, providing a seamless comparison experience. The tradeoff is a slightly increased complexity in code to handle the synchronization.
  • Method 3: Overlaying Different Types of Glyphs. Overlaying glyphs is a compelling way to compare two datasets visually, but care must be taken to prevent one glyph from overshadowing the other, potentially obscuring data.
  • Method 4: Customizing Twin Axes for Enhanced Readability. Customize each axis to improve readability allows for a high degree of fine-tuning but may require additional design considerations to ensure overall coherence of the visualization.
  • Bonus Method 5: Adding a Simple Secondary Axis. This is the quickest method for adding twin axes when the datasets share the same range. It is simple to use but limited to scenarios where scale synchronization is a non-issue.