Я строю данные на гистограмме. Данные сгруппированы в два уровня, где один уровень - год. Я добавляю ползунок диапазона, чтобы изменить x_range графика относительно того, какие годы показывать. Это я пытался реализовать с помощью обратного вызова CustomJS (впервые я пробую CustomJS).
С помощью ползунка коэффициенты на оси X обновляются, как и ожидалось. Однако если я затем использую инструмент масштабирования, а затем использую инструмент сброса, я получаю сообщение об ошибке в веб-консоли:
Ошибка: дублирующий фактор или субфактор: 2016
Не уверен, что я делаю неправильно с настройкой данных. Неправильно ли обновлено значение диапазона коэффициентов?
Я использую версию 1.1.0 Bokeh на MacOS. Та же ошибка наблюдается в Safari и Firefox.
Приведенный ниже код воспроизводит ошибку.
from bokeh.models import ColumnDataSource, FactorRange, RangeSlider, CustomJS
from bokeh.plotting import figure
from bokeh.layouts import column
import pandas as pd
output_file("grouped_customJS.html")
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
data = {'fruits' : fruits,
'2015' : [2, 1, 4, 3, 2, 4],
'2016' : [5, 3, 3, 2, 4, 6],
'2017' : [3, 2, 4, 4, 5, 3]}
df = pd.DataFrame.from_dict(data)
df=df.set_index('fruits').stack().reset_index()
df=df.rename(columns={'level_1':'year', 0:'value'})
# add year as int column for slider
df['year_int'] = df['year'].astype(int)
df=df.set_index(['fruits','year'])
cats = df.index.values
source = ColumnDataSource(
data = {
'categories': cats,
'values': df['value'],
'year': df['year_int']
}
)
p = figure(
x_range=FactorRange(*cats),
plot_height=250,
title="Fruit Counts by Year",
)
p.vbar(x='categories', top='values', width=0.9, source=source)
p.y_range.start = 0
p.x_range.range_padding = 0.1
p.xaxis.major_label_orientation = 1
p.xgrid.grid_line_color = None
slider = RangeSlider(
start=df['year_int'].min(),
end=df['year_int'].max(),
step = 1,
value = (df['year_int'].min(), df['year_int'].max()),
)
callback = CustomJS(args=dict(slider=slider, source=source, plt = p), code="""
plt.x_range.factors = [];
for (var i = 0; i < source.get_length(); i++){
if (source.data['year'][i] >= slider.value[0] && source.data['year'][i] <= slider.value[1]){
plt.x_range.factors.push(source.data['categories'][i]);
}
}
""")
slider.js_on_change('value', callback)
p.x_range.js_on_change('factors', callback)
show(column(p, slider))