如何用Bokeh复选框基于多DataFrame分类列切换条形图显示
Here's a practical implementation that lets you toggle bar visibility using checkboxes for multiple categorical columns. The solution uses Bokeh's CustomJS to handle the multi-category filtering logic without needing deep JavaScript expertise.
Step 1: Setup Dependencies and Sample Data
First, import required modules and create a sample DataFrame matching your structure:
from bokeh.models import ColumnDataSource, CheckboxGroup, CustomJS from bokeh.plotting import figure, show from bokeh.layouts import column import pandas as pd import numpy as np # Generate sample data matching your schema np.random.seed(42) sample_ids = [f"Sample {i}" for i in range(1, 11)] y_values = np.random.randint(10, 50, size=10) sizes = np.random.choice(['big', 'small'], size=10) designs = np.random.choice(['A', 'B'], size=10) colors = np.random.choice(['blue', 'red'], size=10) df = pd.DataFrame({ 'x': sample_ids, 'y': y_values, 'size': sizes, 'design': designs, 'color': colors })
Step 2: Create Data Source and Base Plot
We add a visible column to our data source to track which bars should be displayed:
# Create data source with visibility tracking source = ColumnDataSource(data=dict( x=df['x'], y=df['y'], size=df['size'], design=df['design'], color=df['color'], visible=[True]*len(df) # Start with all bars visible )) # Create the bar plot p = figure( x_range=sample_ids, title="Sample ID vs Y Value (Filterable by Categories)", x_axis_label="Sample ID", y_axis_label="Y Value", height=400, width=800 ) p.vbar(x='x', top='y', width=0.5, source=source, visible='visible')
Step 3: Add Checkbox Group
Create checkboxes for all categorical values:
# Define checkbox group with all category options checkbox_group = CheckboxGroup( labels=['big', 'small', 'A', 'B', 'blue', 'red'], active=[0,1,2,3,4,5], # All checked by default width=150 )
Step 4: CustomJS Callback for Filtering
This callback checks if each bar meets all selected category criteria and updates visibility accordingly:
callback = CustomJS(args=dict(source=source, checkbox=checkbox_group), code=""" const data = source.data; const active_checkboxes = checkbox.active; // Map checkbox indices to category values const selected_sizes = new Set(); if (active_checkboxes.includes(0)) selected_sizes.add('big'); if (active_checkboxes.includes(1)) selected_sizes.add('small'); const selected_designs = new Set(); if (active_checkboxes.includes(2)) selected_designs.add('A'); if (active_checkboxes.includes(3)) selected_designs.add('B'); const selected_colors = new Set(); if (active_checkboxes.includes(4)) selected_colors.add('blue'); if (active_checkboxes.includes(5)) selected_colors.add('red'); // Update visibility for each bar const visible = data['visible']; const sizes = data['size']; const designs = data['design']; const colors = data['color']; for (let i = 0; i < sizes.length; i++) { visible[i] = selected_sizes.has(sizes[i]) && selected_designs.has(designs[i]) && selected_colors.has(colors[i]); } // Trigger plot update source.change.emit(); """) # Link callback to checkbox changes checkbox_group.js_on_change('active', callback)
Full Working Code
Combine all parts and display the interactive plot:
from bokeh.models import ColumnDataSource, CheckboxGroup, CustomJS from bokeh.plotting import figure, show from bokeh.layouts import column import pandas as pd import numpy as np # Generate sample data np.random.seed(42) sample_ids = [f"Sample {i}" for i in range(1, 11)] y_values = np.random.randint(10, 50, size=10) sizes = np.random.choice(['big', 'small'], size=10) designs = np.random.choice(['A', 'B'], size=10) colors = np.random.choice(['blue', 'red'], size=10) df = pd.DataFrame({ 'x': sample_ids, 'y': y_values, 'size': sizes, 'design': designs, 'color': colors }) # Create data source source = ColumnDataSource(data=dict( x=df['x'], y=df['y'], size=df['size'], design=df['design'], color=df['color'], visible=[True]*len(df) )) # Create plot p = figure( x_range=sample_ids, title="Filterable Bar Chart", x_axis_label="Sample ID", y_axis_label="Y Value", height=400, width=800 ) p.vbar(x='x', top='y', width=0.5, source=source, visible='visible') # Create checkboxes checkbox_group = CheckboxGroup( labels=['big', 'small', 'A', 'B', 'blue', 'red'], active=[0,1,2,3,4,5], width=150 ) # Callback logic callback = CustomJS(args=dict(source=source, checkbox=checkbox_group), code=""" const data = source.data; const active_checkboxes = checkbox.active; const selected_sizes = new Set(); if (active_checkboxes.includes(0)) selected_sizes.add('big'); if (active_checkboxes.includes(1)) selected_sizes.add('small'); const selected_designs = new Set(); if (active_checkboxes.includes(2)) selected_designs.add('A'); if (active_checkboxes.includes(3)) selected_designs.add('B'); const selected_colors = new Set(); if (active_checkboxes.includes(4)) selected_colors.add('blue'); if (active_checkboxes.includes(5)) selected_colors.add('red'); const visible = data['visible']; const sizes = data['size']; const designs = data['design']; const colors = data['color']; for (let i = 0; i < sizes.length; i++) { visible[i] = selected_sizes.has(sizes[i]) && selected_designs.has(designs[i]) && selected_colors.has(colors[i]); } source.change.emit(); """) checkbox_group.js_on_change('active', callback) # Display layout show(column(checkbox_group, p))
Key Logic Explanation
- Each checkbox maps to a specific categorical value (e.g., index 0 = 'big', index 2 = 'A').
- We use
Setobjects to efficiently check if a bar's category values are selected. - A bar remains visible only if all its category values match the selected checkboxes (e.g., a bar with size='big', design='A', color='red' will only show if all three corresponding checkboxes are checked).
- The
source.change.emit()command tells Bokeh to refresh the plot with updated visibility values.
Content of the question originates from Stack Exchange, question author Rory Majule

