Bokeh交互式可视化图表不更新问题求助
Hey there! Let's figure out why your Bokeh Select tool isn't updating your COVID stats chart—this is a super common gotcha when you're just starting out with Bokeh's interactivity, so you're not alone here.
First, let's break down the core issue: Bokeh charts are data-source driven. That means every element you see (lines, bars, etc.) is tied to a ColumnDataSource. If your chart isn't updating when you switch the Select option, it almost always means your callback isn't properly modifying this data source, or isn't telling Bokeh that the data has changed.
Let's walk through a fixed example based on what you described, plus explain the key fixes:
Example Fixed Code (Static HTML with JS Callback)
This works for generating a standalone HTML file that doesn't need a server:
from bokeh.plotting import figure, show, output_file from bokeh.models import ColumnDataSource, Select, CustomJS from bokeh.layouts import column import pandas as pd # Mock COVID data (replace this with your actual dataset) data = { 'Date': pd.date_range(start='2020-01-01', periods=100), 'US_cases': [i*100 for i in range(100)], 'US_deaths': [i*10 for i in range(100)], 'India_cases': [i*80 for i in range(100)], 'India_deaths': [i*8 for i in range(100)] } df = pd.DataFrame(data) # Initialize the data source with default country (US) source = ColumnDataSource(data=dict( x=df['Date'], cases=df['US_cases'], deaths=df['US_deaths'] )) # Prepare all country data for the JS callback (so it can access it client-side) all_country_data = { 'US': {'cases': df['US_cases'].tolist(), 'deaths': df['US_deaths'].tolist()}, 'India': {'cases': df['India_cases'].tolist(), 'deaths': df['India_deaths'].tolist()} } # Create the plot p = figure(x_axis_type='datetime', title='COVID-19 Cases & Deaths by Country', x_axis_label='Date', y_axis_label='Number of People') p.line('x', 'cases', source=source, color='#1f77b4', legend_label='Confirmed Cases') p.line('x', 'deaths', source=source, color='#ff4b5c', legend_label='Deaths') # Add the Select dropdown country_select = Select(title='Choose a Country:', value='US', options=['US', 'India']) # The critical part: CustomJS callback to update the data source update_callback = CustomJS( args=dict(source=source, all_data=all_country_data), code=""" // Get the selected country from the dropdown const selected = cb_obj.value; // Pull the corresponding data from our prepped dataset const new_data = all_data[selected]; // Update the data source's fields source.data.cases = new_data.cases; source.data.deaths = new_data.deaths; // Tell Bokeh the data has changed (this is easy to forget!) source.change.emit(); """ ) # Link the callback to the Select dropdown's value change country_select.js_on_change('value', update_callback) # Combine dropdown and plot into a layout layout = column(country_select, p) output_file('covid_interactive.html') show(layout)
If You Need a Bokeh Server (Python Callback)
If you're doing more complex data processing (like fetching live data), use a Python callback with Bokeh Server:
from bokeh.plotting import figure, curdoc from bokeh.models import ColumnDataSource, Select from bokeh.layouts import column import pandas as pd # Mock COVID data data = { 'Date': pd.date_range(start='2020-01-01', periods=100), 'US_cases': [i*100 for i in range(100)], 'US_deaths': [i*10 for i in range(100)], 'India_cases': [i*80 for i in range(100)], 'India_deaths': [i*8 for i in range(100)] } df = pd.DataFrame(data) source = ColumnDataSource(data=dict( x=df['Date'], cases=df['US_cases'], deaths=df['US_deaths'] )) p = figure(x_axis_type='datetime', title='COVID-19 Cases & Deaths by Country', x_axis_label='Date', y_axis_label='Number of People') p.line('x', 'cases', source=source, color='#1f77b4', legend_label='Confirmed Cases') p.line('x', 'deaths', source=source, color='#ff4b5c', legend_label='Deaths') country_select = Select(title='Choose a Country:', value='US', options=['US', 'India']) def update_plot(attr, old_value, new_value): # Fetch the new data based on selected country new_cases = df[f'{new_value}_cases'] new_deaths = df[f'{new_value}_deaths'] # Update the entire data source (Bokeh detects this change automatically) source.data = dict( x=df['Date'], cases=new_cases, deaths=new_deaths ) # Link the callback to the dropdown country_select.on_change('value', update_plot) curdoc().add_root(column(country_select, p))
Run this with bokeh serve --show your_script.py
Key Fixes to Note
- Always Update the
ColumnDataSource: This is the backbone of Bokeh interactivity. If you don't modify this source, your chart will never change. - Trigger Data Change (JS Callback): In
CustomJS, you must callsource.change.emit()after updating data—this tells Bokeh to re-render the chart. - Use Correct Callback Type: Static HTML needs
CustomJS; server-side logic needs Python callbacks with Bokeh Server. - Match Dropdown Options to Data: Make sure your Select's
optionsexactly match the prefix/suffix of your data columns (e.g., "US" maps to "US_cases" and "US_deaths").
If your original code was missing any of these steps, adding them should fix the update issue. If you still run into trouble, feel free to share your exact code and we can tweak it further!
内容的提问来源于stack exchange,提问作者Indranil Mallick

