I have been using Bokeh for 1 year through Jupyter Notebooks, and recently, I have encountered an interesting issue and an even more interesting workaround.
For the last few months, whenever I have been plotting Bokeh plots, they have been rendering much more slowly than I am used to: it takes 5-10 seconds per plot with <100 glyphs and up to 5 minutes per plot with >1000 glyphs. I assumed my computer was slow and that Bokeh had performance limitations.
However, I have discovered that if I first generate the plot variable p
in one cell, then I show the plot in a second cell with bokeh.io.show(p)
, then I interrupt the kernel while running the second cell, and then I rerun the second cell, it plots instantly.
I have reproduced this several times across notebooks and over a few weeks. Has anyone else encountered this?
I am running Windows 10 on a laptop with 16GB RAM (Intel i7-1260) and 12 cores/16 threads (I think). My computing environment is below.
$ jupyter --version
Selected Jupyter core packages...
IPython : 8.20.0
ipykernel : 6.28.0
ipywidgets : 8.1.2
jupyter_client : 8.6.0
jupyter_core : 5.5.0
jupyter_server : 2.10.0
jupyterlab : 4.0.11
nbclient : 0.8.0
nbconvert : 7.10.0
nbformat : 5.9.2
notebook : 7.0.8
qtconsole : 5.5.1
traitlets : 5.7.1
> $ python --version
Python 3.11.8
(The below was run in Jupyter)
> $ bokeh.io.output_notebook()
BokehJS 3.4.0 successfully loaded.
In case it is relevant, I have been building all of my plots with a function I coded for convenience:
def generate_plot(x_range, x_axis_type, x_label, y_range, y_axis_type, y_label, title, font_size=20, height = 325, width = 325, x_interval = None, y_interval = None):
"""This function takes in x axis data, y axis data, and a title, and simply
returns a Bokeh plot object with customization to make the graphs readable."""
if y_range and x_range:
p = bokeh.plotting.figure(width = width, height = height, y_range = y_range, x_range = x_range, title = title,
x_axis_label = x_label, y_axis_label = y_label, x_axis_type = x_axis_type, y_axis_type = y_axis_type)
elif y_range and not x_range:
p = bokeh.plotting.figure(width = width, height = height, y_range = y_range, title = title,
x_axis_label = x_label, y_axis_label = y_label, x_axis_type = x_axis_type, y_axis_type = y_axis_type)
elif x_range and not y_range:
p = bokeh.plotting.figure(width = width, height = height, x_range = x_range, title = title,
x_axis_label = x_label, y_axis_label = y_label, x_axis_type = x_axis_type, y_axis_type = y_axis_type)
else:
p = bokeh.plotting.figure(width = width, height = height, title = title,
x_axis_label = x_label, y_axis_label = y_label, x_axis_type = x_axis_type, y_axis_type = y_axis_type)
font_size = "{0:}px".format(font_size)
p.title.align, p.title.text_font, p.title.text_font_style = "center", "aptos", "bold" # Title text options
p.title.text_font_size = "24px" # Title text size
p.xaxis.axis_label_text_font, p.xaxis.axis_label_text_font_style = "aptos", "bold" # X-axis label text options
p.xaxis.major_label_text_font, p.xaxis.major_label_text_font_style = "aptos", "bold" # X-axis tick labels text options
p.xaxis.axis_label_text_font_size, p.xaxis.major_label_text_font_size = font_size, font_size # Y-axis & tick label sizes
p.yaxis.axis_label_text_font, p.yaxis.axis_label_text_font_style = "aptos", "bold" # Y-axis label text options
p.yaxis.major_label_text_font, p.yaxis.major_label_text_font_style = "aptos", "bold" # Y-axis tick labels text options
p.yaxis.axis_label_text_font_size, p.yaxis.major_label_text_font_size = font_size, font_size # Y-axis & tick label sizes
p.xgrid.visible, p.ygrid.visible, p.outline_line_color = False, False, None # X grid, Y grid, and outline visibility
p.ygrid.minor_grid_line_color = p.xgrid.minor_grid_line_color = p.ygrid.grid_line_color
p.ygrid.grid_line_color = p.xgrid.grid_line_color = "grey"
p.outline_line_color = p.background_fill_color # Make outline invisible vs. background
if x_interval: p.xaxis.ticker.min_interval = p.xaxis.ticker.max_interval = x_interval# Set the interval between major x-axis ticks to the value 'x_interval', if it has been given
if y_interval: p.yaxis.ticker.min_interval = p.yaxis.ticker.max_interval = y_interval # Set the interval between major y-axis ticks to the value 'y_interval', if it has been given