Как построить взаимозависимые мультиселектные меню в Боке - PullRequest
0 голосов
/ 16 апреля 2020

Я изо всех сил пытаюсь внедрить взаимозависимые мультиселектные меню в Боке. Приведенный ниже код почти работает, но когда я выбираю данный вид в первом множественном меню (скажем, вид E), а затем выбираю набор во втором множественном меню (набор 1), он возвращает все виды для этого набора (A и E) вместо возврата только выбранных видов в первом меню (E). Я не уверен, как решить эту проблему. Мысли?

# Data handling
import pandas as pd

# Bokeh libraries
from bokeh.plotting import figure, output_file, show
from bokeh.plotting import figure

from bokeh.models import ColumnDataSource, CustomJS, Legend
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.layouts import column, row
from bokeh.models.widgets import MultiSelect, Button


# test data
original_data = dict(species=['A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','B','C','D','E','A','A'],
                     length=[2, 10, 20, 40, 60, 80, 70, 50, 15, 36, 76, 74, 72, 44, 36, 18, 40, 64, 40, 64, 40, 30, 120],
                     weight=[2, 100, 150, 200, 420, 700, 600, 300, 200, 200, 620, 610, 601, 610, 601, 80, 205, 80, 800, 700, 240, 160, 800],
                     set_number=['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '3', '4', '1', '5', '1'],
                     sex=[2, 0, 0, 0, 2, 1, 1, 0, 2, 1, 1, 1, 0, 1, 1, 0, 2, 1, 1, 2, 1, 0, 2])

original_data = pd.DataFrame(original_data)
original_data['color'] = 'black'

original_source = ColumnDataSource(original_data)

# Empty source so the plot is empty before the data is selected
source = ColumnDataSource(dict(species=[], set_number=[], length=[], weight=[], sex=[], color=[]))

# Main plot
plot = figure(title='Length-weight scatterplot', x_axis_label='length (cm)', y_axis_label='weight (g)')

plot.circle(x='length', y='weight', color='color', fill_alpha=1, source=source, line_width=2)

# Select species
available_species = list(set(original_data['species']))
available_species.sort()

code = """
var data = original_source.data;
var source_data = source.data;

var species_data = data['species'];
var length_data = data['length'];
var weight_data = data['weight'];
var sex_data = data['sex'];
var set_number_data = data['set_number'];
var color_data = data['color'];
var selected_species = cb_obj.value;

var source_length = source_data['length'];
source_length.length = 0;

var source_weight = source_data['weight'];
source_weight.length = 0;

var source_sex = source_data['sex'];
source_sex.length = 0;

var source_species = source_data['species'];
source_species.length = 0;

var source_set_number = source_data['set_number'];
source_set_number.length = 0;

var source_color = source_data['color'];
source_color.length = 0;
var unique_sets = [];
  for (var i = 0; i < length_data.length; i++) {
    if (selected_species.indexOf(species_data[i]) >= 0)  {
      source_length.push(length_data[i]);
      source_weight.push(weight_data[i]);
      source_species.push(species_data[i]);
      source_set_number.push(set_number_data[i]);
      source_sex.push(sex_data[i]);
      source_color.push(color_data[i]);

      if ( !unique_sets.includes(set_number_data[i]) ) {
        unique_sets.push(set_number_data[i])
        }
    }
  }
  source.change.emit();
  multiselect_set.options = unique_sets
  """

print(source.data['set_number'])

multiselect_species = MultiSelect(title='Species:', value=[], options=available_species, width=235)

# Select set (multiselect)
available_set = list(set(original_data['set_number']))
available_set.sort()

set_callback = CustomJS(args={'source': source, 'original_source': original_source},
                        code="""

                         var data = original_source.data;
                         var source_data = source.data;

                         var species_data = data['species'];
                         var length_data = data['length'];
                         var weight_data = data['weight'];
                         var sex_data = data['sex'];
                         var set_number_data = data['set_number'];
                         var color_data = data['color'];
                         var selected_species = cb_obj.value;

                         var source_length = source_data['length'];
                         source_length.length = 0;

                         var source_weight = source_data['weight'];
                         source_weight.length = 0;

                         var source_sex = source_data['sex'];
                         source_sex.length = 0;

                         var source_species = source_data['species'];
                         source_species.length = 0;

                         var source_set_number = source_data['set_number'];
                         source_set_number.length = 0;

                         var source_color = source_data['color'];
                         source_color.length = 0;
                           for (var i = 0; i < length_data.length; i++) {
                             if (selected_species.indexOf(set_number_data[i]) >= 0) {
                                 source_length.push(length_data[i]);
                                 source_weight.push(weight_data[i]);
                                 source_species.push(species_data[i]);
                                 source_set_number.push(set_number_data[i]);
                                 source_sex.push(sex_data[i]);
                                 source_color.push(color_data[i]);
                             }
                           }
                                  source.change.emit();
                                  """)

multiselect_set = MultiSelect(title='Set:', value=[], options=available_set, width=235)
multiselect_set.js_on_change('value', set_callback)

species_callback = CustomJS(args={'source': source,
                                  'original_source': original_source,
                                  'multiselect_set': multiselect_set,
                                  },
                            code=code)

# The sets available should be updated depending on the species chosen
multiselect_species.js_on_change('value', species_callback)

# # Clear button to reset multiselect_set
clear_set_button = Button(label="Clear set selection", button_type="primary", width=235)
clear_set_callback = CustomJS(args=dict(s=multiselect_set), code="s.value = []")
clear_set_button.js_on_event('button_click', clear_set_callback)

# Clear button to reset multiselect_species
clear_species_button = Button(label="Clear species selection", button_type="primary", width=235)
clear_species_callback = CustomJS(args=dict(s=multiselect_species), code="s.value = []")
clear_species_button.js_on_event('button_click', clear_species_callback)


# Data table
columns = [TableColumn(field="species", title="common name", width=200),
           TableColumn(field="set_number", title="set number", width=100),
           TableColumn(field="length", title="length (cm)", width=100),
           TableColumn(field="weight", title="weight (g)", width=100),
           TableColumn(field="sex", title="sex", width=50)]

data_table = DataTable(source=source, columns=columns, sortable=True, editable=True, width=500, height=450,
                       fit_columns=True, margin=(100, 0, 0, 0))


# Set up widgets layout
widgets_layout = column(multiselect_species, clear_species_button, multiselect_set, clear_set_button)

# Set up figures layout
figures_layout = row(plot, data_table)

# Set up page layout
page_layout = row(widgets_layout, figures_layout)

show(page_layout)
...