I have a script that plots a few series voltage vs. speed on a bokeh plot for comparison. It includes hover functionality for easier review of the data. It ran successfully when i used python 3.8.5 but recently, I've needed to move to a more recent version of python, so I started using 3.9.7.
The way the script works is it pulls data from an excel file I've already populated, after which it plots each series that I've specified the voltage and speed column names for. It creates a linear regression line for the data, then adds a 95% confidence interval to the plot by calculating an upper and lower bound for the dataset and passing a linear regression line through each of those bounds. The regression line and confidence band are based on a different set of columns in the excel. Those columns are comprehensive so they basically just include all the data from all the series that are being plotted. And thats what's used for the regression line & confidence interval that is representative of the whole the dataset. Then the hover tool is added for the confidence band (this is where my issue is). Finally, I append my renderers and open the plot in the browser.
That's the gist of the script, minus some unrelated extras like printing a data table for the confidence band values and shading the confidence interval.
This is the error i get:
ValueError: failed to validate HoverTool(id='1986', ...).renderers: expected an element of either Auto or List(Instance(DataRenderer)), got [Band(id='1908', ...)]
This is the script:
import pandas as pd
import numpy as np
from scipy import stats
from bokeh.plotting import figure, show, output_file, output_notebook
from bokeh.models import HoverTool, ColumnDataSource, Band, CustomJS, Label
from bokeh.layouts import column, gridplot
from bokeh.palettes import Category10
from bokeh.models.widgets import CheckboxGroup
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import norm
from bokeh.io import push_notebook
from sklearn.linear_model import LinearRegression
def plot_voltage_vs_speed(excel_file, sheet_name, series_info, plot_title, ci_info):
df = pd.read_excel(excel_file, sheet_name=sheet_name)
specified_speeds = np.array([3000, 5250, 10500, 14500, 15750, 16250, 18000, 20581, 21000])
renderers = []
checkbox_labels = []
p = figure(title=plot_title, x_axis_label='Speed (RPM)', y_axis_label='Voltage (V)', width=800, height=600)
for series in series_info:
voltage_col = series['voltage_col']
speed_col = series['speed_col']
label = series['label']
x = df[speed_col]
y = df[voltage_col]
circle_renderer = p.circle(x, y, legend_label=label, fill_color="white", size=6, color=Category10[10][series_info.index(series)])
renderers.append((circle_renderer,))
checkbox_labels.append(label)
# Adding hover tool for the circles; this hover doesnt give me errors
hover = HoverTool(tooltips=[
("Speed", "@x"),
("Voltage", "@y")
])
p.add_tools(hover)
... # skipping to next relevant portion of script
# Perform linear regression
model = LinearRegression(fit_intercept=False)
model.fit(x_filtered.values.reshape(-1, 1), y_filtered)
line = model.predict(x_filtered.values.reshape(-1, 1))
# Calc standard deviation and mean for each speed
std_devs = df_filtered.groupby('mapped_speed')[ci_info['voltage_col']].std()
means = df_filtered.groupby('mapped_speed')[ci_info['voltage_col']].mean()
# Calculate confidence bands
upper_bound = means.loc[x_filtered].values + 1.96 * std_devs.loc[x_filtered].values
lower_bound = means.loc[x_filtered].values - 1.96 * std_devs.loc[x_filtered].values
# Perform linear regression for upper and lower bounds
model_upper = LinearRegression(fit_intercept=False)
model_upper.fit(x_filtered.values.reshape(-1, 1), upper_bound)
upper_line = model_upper.predict(x_filtered.values.reshape(-1, 1))
model_lower = LinearRegression(fit_intercept=False)
model_lower.fit(x_filtered.values.reshape(-1, 1), lower_bound)
lower_line = model_lower.predict(x_filtered.values.reshape(-1, 1))
sorted_indices = np.argsort(x_filtered)
x_sorted = x_filtered.values[sorted_indices]
line_sorted = line[sorted_indices]
lower_sorted = lower_bound[sorted_indices]
upper_sorted = upper_bound[sorted_indices]
# Create regression line and confidence band
source = ColumnDataSource(data={
'x': x_sorted,
'line': line_sorted,
'lower': lower_sorted,
'upper': upper_sorted
})
line_renderer = p.line('x', 'line', source=source, legend_label="Regression Line", line_width=1, color="black")
# This is where band comes in. I used it later when adding the hover tool for the confidence band.
band = Band(base='x', lower='lower', upper='upper', source=source, level='underlay', fill_alpha=0.2, line_width=1, line_color="black", fill_color="gray")
p.add_layout(band)
''' Adding hover tool for confidence band; this is the hover that gives me an error, specifically the renderers=[band] part of it.'''
hover_band = HoverTool(renderers=[band], tooltips=[
("Speed", "@x"),
("Lower Bound", "@lower"),
("Upper Bound", "@upper")
])
p.add_tools(hover_band)
renderers.append((line_renderer,))
renderers.append((band,))
checkbox_labels.append("Regression Line")
checkbox_labels.append("Prediction Interval")
checkbox_group = CheckboxGroup(labels=checkbox_labels, active=list(range(len(renderers))))
checkbox_callback = CustomJS(args=dict(renderers=renderers, checkbox_group=checkbox_group), code="""
for (let i = 0; i < renderers.length; i++) {
const elements = renderers[i];
const visible = checkbox_group.active.includes(i);
for (let elem of elements) {
elem.visible = visible;
}
}
""")
checkbox_group.js_on_change('active', checkbox_callback)
layout = column(checkbox_group, p)
# Show plot in notebook + browser
output_notebook()
show(layout)
output_file("Machine_Compare.html")
excel_file = 'Machine_Comparison.xlsx'
sheet_name = 'Voltage_Comparison' # Specify sheet
series_info = [
{'voltage_col': 'Voltage1', 'speed_col': 'Speed1', 'label': 'Machine 1'},
{'voltage_col': 'Voltage2', 'speed_col': 'Speed2', 'label': 'Machine 2'}
]
plot_title = 'Machine Comparison with 95% Prediction Interval'
# Columns for prediction interval calc
ci_info = {'voltage_col': 'Voltages', 'speed_col': 'Speeds'}
plot_voltage_vs_speed(excel_file, sheet_name, series_info, plot_title, ci_info)
Sorry its a bit long. The relevant parts were spread out around the script and only copying those over made it somewhat confusing to read, without the steps in between.
I tried removing renderers=[band], from the hover tool that causes the error to ensure I had the cause of the error right, and though it did run and generate the plot successfully, the hover information showed ?? for both speed and voltage values.
I have a script that plots a few series voltage vs. speed on a bokeh plot for comparison. It includes hover functionality for easier review of the data. It ran successfully when i used python 3.8.5 but recently, I've needed to move to a more recent version of python, so I started using 3.9.7.
The way the script works is it pulls data from an excel file I've already populated, after which it plots each series that I've specified the voltage and speed column names for. It creates a linear regression line for the data, then adds a 95% confidence interval to the plot by calculating an upper and lower bound for the dataset and passing a linear regression line through each of those bounds. The regression line and confidence band are based on a different set of columns in the excel. Those columns are comprehensive so they basically just include all the data from all the series that are being plotted. And thats what's used for the regression line & confidence interval that is representative of the whole the dataset. Then the hover tool is added for the confidence band (this is where my issue is). Finally, I append my renderers and open the plot in the browser.
That's the gist of the script, minus some unrelated extras like printing a data table for the confidence band values and shading the confidence interval.
This is the error i get:
ValueError: failed to validate HoverTool(id='1986', ...).renderers: expected an element of either Auto or List(Instance(DataRenderer)), got [Band(id='1908', ...)]
This is the script:
import pandas as pd
import numpy as np
from scipy import stats
from bokeh.plotting import figure, show, output_file, output_notebook
from bokeh.models import HoverTool, ColumnDataSource, Band, CustomJS, Label
from bokeh.layouts import column, gridplot
from bokeh.palettes import Category10
from bokeh.models.widgets import CheckboxGroup
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import norm
from bokeh.io import push_notebook
from sklearn.linear_model import LinearRegression
def plot_voltage_vs_speed(excel_file, sheet_name, series_info, plot_title, ci_info):
df = pd.read_excel(excel_file, sheet_name=sheet_name)
specified_speeds = np.array([3000, 5250, 10500, 14500, 15750, 16250, 18000, 20581, 21000])
renderers = []
checkbox_labels = []
p = figure(title=plot_title, x_axis_label='Speed (RPM)', y_axis_label='Voltage (V)', width=800, height=600)
for series in series_info:
voltage_col = series['voltage_col']
speed_col = series['speed_col']
label = series['label']
x = df[speed_col]
y = df[voltage_col]
circle_renderer = p.circle(x, y, legend_label=label, fill_color="white", size=6, color=Category10[10][series_info.index(series)])
renderers.append((circle_renderer,))
checkbox_labels.append(label)
# Adding hover tool for the circles; this hover doesnt give me errors
hover = HoverTool(tooltips=[
("Speed", "@x"),
("Voltage", "@y")
])
p.add_tools(hover)
... # skipping to next relevant portion of script
# Perform linear regression
model = LinearRegression(fit_intercept=False)
model.fit(x_filtered.values.reshape(-1, 1), y_filtered)
line = model.predict(x_filtered.values.reshape(-1, 1))
# Calc standard deviation and mean for each speed
std_devs = df_filtered.groupby('mapped_speed')[ci_info['voltage_col']].std()
means = df_filtered.groupby('mapped_speed')[ci_info['voltage_col']].mean()
# Calculate confidence bands
upper_bound = means.loc[x_filtered].values + 1.96 * std_devs.loc[x_filtered].values
lower_bound = means.loc[x_filtered].values - 1.96 * std_devs.loc[x_filtered].values
# Perform linear regression for upper and lower bounds
model_upper = LinearRegression(fit_intercept=False)
model_upper.fit(x_filtered.values.reshape(-1, 1), upper_bound)
upper_line = model_upper.predict(x_filtered.values.reshape(-1, 1))
model_lower = LinearRegression(fit_intercept=False)
model_lower.fit(x_filtered.values.reshape(-1, 1), lower_bound)
lower_line = model_lower.predict(x_filtered.values.reshape(-1, 1))
sorted_indices = np.argsort(x_filtered)
x_sorted = x_filtered.values[sorted_indices]
line_sorted = line[sorted_indices]
lower_sorted = lower_bound[sorted_indices]
upper_sorted = upper_bound[sorted_indices]
# Create regression line and confidence band
source = ColumnDataSource(data={
'x': x_sorted,
'line': line_sorted,
'lower': lower_sorted,
'upper': upper_sorted
})
line_renderer = p.line('x', 'line', source=source, legend_label="Regression Line", line_width=1, color="black")
# This is where band comes in. I used it later when adding the hover tool for the confidence band.
band = Band(base='x', lower='lower', upper='upper', source=source, level='underlay', fill_alpha=0.2, line_width=1, line_color="black", fill_color="gray")
p.add_layout(band)
''' Adding hover tool for confidence band; this is the hover that gives me an error, specifically the renderers=[band] part of it.'''
hover_band = HoverTool(renderers=[band], tooltips=[
("Speed", "@x"),
("Lower Bound", "@lower"),
("Upper Bound", "@upper")
])
p.add_tools(hover_band)
renderers.append((line_renderer,))
renderers.append((band,))
checkbox_labels.append("Regression Line")
checkbox_labels.append("Prediction Interval")
checkbox_group = CheckboxGroup(labels=checkbox_labels, active=list(range(len(renderers))))
checkbox_callback = CustomJS(args=dict(renderers=renderers, checkbox_group=checkbox_group), code="""
for (let i = 0; i < renderers.length; i++) {
const elements = renderers[i];
const visible = checkbox_group.active.includes(i);
for (let elem of elements) {
elem.visible = visible;
}
}
""")
checkbox_group.js_on_change('active', checkbox_callback)
layout = column(checkbox_group, p)
# Show plot in notebook + browser
output_notebook()
show(layout)
output_file("Machine_Compare.html")
excel_file = 'Machine_Comparison.xlsx'
sheet_name = 'Voltage_Comparison' # Specify sheet
series_info = [
{'voltage_col': 'Voltage1', 'speed_col': 'Speed1', 'label': 'Machine 1'},
{'voltage_col': 'Voltage2', 'speed_col': 'Speed2', 'label': 'Machine 2'}
]
plot_title = 'Machine Comparison with 95% Prediction Interval'
# Columns for prediction interval calc
ci_info = {'voltage_col': 'Voltages', 'speed_col': 'Speeds'}
plot_voltage_vs_speed(excel_file, sheet_name, series_info, plot_title, ci_info)
Sorry its a bit long. The relevant parts were spread out around the script and only copying those over made it somewhat confusing to read, without the steps in between.
I tried removing renderers=[band], from the hover tool that causes the error to ensure I had the cause of the error right, and though it did run and generate the plot successfully, the hover information showed ?? for both speed and voltage values.
Share Improve this question edited Mar 25 at 17:17 Zay asked Mar 24 at 13:11 ZayZay 114 bronze badges 1 |1 Answer
Reset to default 0So what wound up working for me was replacing [band] with [line_renderer]. I assume the problem was because Band was no longer a valid target for bokeh's hover tool as bigreddot mentioned in their comment.
So that section of the code now looks like this:
# Adding hover tool for confidence band
hover_band = HoverTool(renderers=[line_renderer], tooltips=[
("Speed", "@x"),
("Lower Bound", "@lower"),
("Upper Bound", "@upper")
])
p.add_tools(hover_band)
Band
is not a valid target for a hover tool, only glyphs (e.g. scatter, line, patch, bars, etc) are. And that???
in the hover output means that the column name you configured for the tool is missing from theColumnDataSource
backing the glyph. – bigreddot Commented Mar 24 at 16:42