You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bokeh回调中动态更新多线图时遇属性数组检索错误

Hey there, let's tackle this Bokeh dynamic multi-line plot issue you're facing. That "attempted to retrieve property array for none..." error usually pops up when your glyphs or legends are still clinging to old column names that don't exist in the new dataset you're switching to. Since your datasets have variable columns and data types, we need a flexible approach that fully resets and rebuilds the plot elements whenever the country selection changes.

Core Issue Breakdown

When you switch datasets without cleaning up old plot components:

  • Existing Line glyphs still reference column names from the previous country's data
  • The legend remains linked to these outdated glyphs
  • Bokeh tries to fetch data for columns that no longer exist in the new ColumnDataSource, triggering the error

Step-by-Step Solution

We'll build a dynamic system that:

  1. Tracks current plot renderers to clean them up on switch
  2. Rebuilds glyphs and legend items based on the active dataset's columns
  3. Fully replaces the data source with the new country's data

Full Working Code Example

from bokeh.io import curdoc
from bokeh.models import ColumnDataSource, Select, Legend
from bokeh.plotting import figure
from bokeh.layouts import column
import pandas as pd

# Sample datasets with variable columns (matching your use case)
country_datasets = {
    "USA": pd.DataFrame({
        "year": [2010, 2011, 2012, 2013],
        "gdp": [15, 15.5, 16, 16.5],
        "unemployment": [9, 8.5, 8, 7.5]
    }),
    "Japan": pd.DataFrame({
        "year": [2010, 2011, 2012, 2013],
        "gdp": [5, 5.2, 5.1, 5.3],
        "exports": [1.2, 1.3, 1.1, 1.4],
        "inflation": [0.1, 0.2, -0.1, 0]
    }),
    "India": pd.DataFrame({
        "year": [2010, 2011, 2012, 2013],
        "gdp": [1.5, 1.7, 1.9, 2.1]
    })
}

# Initialize plot and core components
plot = figure(
    x_axis_label="Year",
    y_axis_label="Value",
    title="Country Economic Metrics",
    width=800,
    height=400
)
source = ColumnDataSource()
renderers = []  # Track active line renderers for cleanup
legend = Legend(items=[], location="top_left")
plot.add_layout(legend)

# Define update logic
def update_plot(selected_country):
    # 1. Clean up old renderers and legend items
    for renderer in renderers:
        plot.renderers.remove(renderer)
    renderers.clear()
    legend.items = []

    # 2. Load new dataset and update data source
    current_data = country_datasets[selected_country]
    source.data = current_data.to_dict("list")

    # 3. Build new lines and legend items
    x_column = "year"
    y_columns = [col for col in current_data.columns if col != x_column]
    color_palette = ["#2c3e50", "#27ae60", "#e74c3c", "#f39c12"]

    for idx, y_col in enumerate(y_columns):
        line = plot.line(
            x=x_column,
            y=y_col,
            source=source,
            color=color_palette[idx % len(color_palette)],
            line_width=2
        )
        renderers.append(line)
        legend.items.append((y_col, [line]))

# Set initial state
initial_country = "USA"
update_plot(initial_country)

# Create selection widget
country_selector = Select(
    title="Select Country:",
    value=initial_country,
    options=list(country_datasets.keys())
)
country_selector.on_change("value", lambda attr, old, new: update_plot(new))

# Assemble layout and run
layout = column(country_selector, plot)
curdoc().add_root(layout)

Key Details to Note

  • Renderer Cleanup: We maintain a renderers list to track all active line glyphs. When switching countries, we remove these from the plot entirely to eliminate references to old columns.
  • Dynamic Legend Building: The legend is rebuilt from scratch each time, ensuring only columns present in the current dataset are included.
  • Full Data Source Replacement: Since column counts vary, we don't use patch()—we fully replace the source.data with the new dataset's dictionary representation.
  • Bokeh Server Requirement: This uses Python callbacks, so you'll need to run it with bokeh serve --show your_script.py (static HTML with CustomJS is far less flexible for variable column scenarios).

Additional Tips

  • Ensure your x-axis column (e.g., year) exists in all datasets. If even the x-column varies, add logic to detect and update the x parameter in the line() call.
  • For large datasets, consider optimizing by reusing glyphs instead of rebuilding, but for variable columns, full cleanup/rebuild is the most reliable approach.

内容的提问来源于stack exchange,提问作者Gaurab Ghosh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:02:09