Bokeh: Дублирующий фактор или ошибка субфактора с CustomJS изменением x_range - PullRequest
0 голосов
/ 05 мая 2019

Я строю данные на гистограмме. Данные сгруппированы в два уровня, где один уровень - год. Я добавляю ползунок диапазона, чтобы изменить 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))

1 Ответ

2 голосов
/ 06 мая 2019

Попробуйте это (отлично работает с Bokeh v1.1.0):

callback = CustomJS(args = dict(slider = slider, source = source, plt = p), code = """
    var 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]){
            factors.push(source.data['categories'][i]);
        }
    }
    plt.x_range.factors = factors; """)
...