bokeh: Can't change type of figure.x_range dynamically
ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
$ bokeh info
Python version : 3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24)
IPython version : 7.1.1
Tornado version : 5.1.1
Bokeh version : 1.0.1
BokehJS static path : /path/to/my/python3.7/site-packages/bokeh/server/static
node.js version : v9.7.1
npm version : 5.6.0
Description of expected behavior and the observed behavior
I am trying to implement a plot where the user can select a column from a dropdown and see the histogram of values in the column. Some columns may be categorical, and some are continuous, so I’ve using a vbar and setting the width accordingly. However, this means that I sometimes need a DataRange1d, and sometimes a FactorRange. Changing my figure’s x_range to a FactorRange causes my plot to be reset and not show any output. There is no error, the glyphs just disappear.
Complete, minimal, self-contained example code that reproduces the issue
This is about as minimal as I could get it, sorry.
from bokeh.plotting import Figure, ColumnDataSource
from bokeh.models.widgets import Dropdown
from bokeh.models.ranges import DataRange1d, FactorRange
from bokeh.layouts import row
from bokeh.io import curdoc
import pandas as pd
import numpy as np
def _col_stats(col,):
"""Value counts or histogram of column"""
is_str = isinstance(col.values[0], str)
if is_str:
counts = col.value_counts().sort_index()
hist = counts.values
edges = np.array(counts.index)
else:
hist, edges = np.histogram(col, bins="auto")
return hist, edges
def _hist_source_dict(hist, edges):
if isinstance(edges[0], str):
return {"top": hist, "x": edges, "width": 0.8 * np.ones_like(edges)}
else:
w = np.diff(edges)
x = np.array(edges[:-1]) + w / 2
return {"top": hist, "x": x, "width": w}
def _update_histogram(figure, source, hist, edges, name=""):
"""Why doesn't this work?"""
if isinstance(edges[0], str):
figure.x_range = FactorRange(factors=edges)
else:
figure.x_range = DataRange1d()
source.data = _hist_source_dict(hist, edges)
if name:
figure.title.text = name
df = pd.DataFrame(
{"a": [1, 2, 3, 4, 3, 1, 2, 3, 4, 1], "b": list("abcdcbaeda")}
)
columns = df.columns
# initial values
first_col = columns[0]
first_hist, first_edges = _col_stats(df[first_col])
# set up data sources
hist_source = ColumnDataSource(data=_hist_source_dict(first_hist, first_edges))
# set up dashboard components
plot = Figure(
title=first_col,
tools="xpan,xwheel_zoom,box_zoom,reset",
active_drag="xpan",
active_scroll="xwheel_zoom",
)
plot.vbar(top="top", x="x", width="width", bottom=0, source=hist_source)
dropdown = Dropdown(
label="Column", menu=[(col, col) for col in df.columns], value=first_col
)
# configure callbacks
def dropdown_callback(attr, old, new):
global df, plot, hist_source
hist, edges = _col_stats(df[new])
_update_histogram(plot, hist_source, hist, edges, new)
dropdown.on_change("value", dropdown_callback)
# Position elements in document
root = row(plot, dropdown)
curdoc().add_root(root)
If you save this and run it with bokeh serve, selecting ‘b’ from the dropdown will blank out the plot, but there is no error.
Screenshots or screencasts of the bug in action
Continuous column:

Categorical column:

About this issue
- Original URL
- State: open
- Created 6 years ago
- Comments: 15 (6 by maintainers)
Not quite shure if the issue has been solved, but a way to get around is by overwriting the factor attribute of the figure.x_renge object. so for your particular case will look something like :
figure.x_range.factors = edgesThis on the docs is not completely clear and its necessary an example that covers this figure() related modifications made dynamically, i’ll be glad to share an example of this.
I’ve been attempting to update fig{x,y}_range.{start,end} in a callback function in a mapping application, to show different state maps in the correct aspect ratio. I update the data source for the figure, then directly update the x_range.start, etc. Running this under my bokeh server (2.0.1) this works, but randomly fails. Sometimes all limits are refreshed and the map displays properly. Other times, one or more limit fails to update, leading to a distorted map or no visible rendering of the state. This may be related to the problem reported by @mattpap above. It appears to be unresolved.
I’m currently linking/unlinking my x-ranges with a button with two functions:
Where
s1ands2are my two figures. Is this the way to do it or is there a ‘better’ way?