如何为Bokeh多折线图配置HoverTool及多Y轴悬浮显示?
Let's break down and fix your issues step by step:
1. Fix the ColumnDataSource Length Mismatch Warning
Your initial warning pops up because you packed two lists (pop_pakistan and pop_india) into a single population column—this makes its length 2, while year has 6 entries. ColumnDataSource requires all columns to share the same length.
The correct approach is to store each country's population as a separate column in the data source:
source = ColumnDataSource(data=dict( year=year, pop_pakistan=pop_pakistan, pop_india=pop_india ))
Now every column has 6 entries, eliminating the mismatch warning entirely.
2. Configure HoverTool to Show Correct Per-Country Data
Your modified code uses the shared data source but doesn’t set up the HoverTool properly to display values specific to each line. Here are two reliable ways to fix this:
Method 1: Single HoverTool with Automatic Series Detection
Use Bokeh’s $name variable to dynamically reference the data column of the hovered glyph. First, set a name parameter for each glyph (matching the column name in your data source), then configure the tooltip to use @$name:
hover = HoverTool( tooltips=[ ("Year", "@year"), ("Country", "$name"), ("Population (million)", "@$name{0.2f}") ], mode='vline' # Optional: Shows tooltip for all points at the hovered x-value ) # Add glyphs with name matching data columns plot.line('year', 'pop_pakistan', line_width=2, line_color='green', legend='Pakistan', source=source, name='Pakistan') plot.circle('year', 'pop_pakistan', fill_color="green", line_color='green', size=8, source=source, name='Pakistan') plot.line('year', 'pop_india', line_width=2, line_color='orange', legend='India', source=source, name='India') plot.circle('year', 'pop_india', fill_color="orange", line_color='orange', size=8, source=source, name='India') plot.add_tools(hover)
The $name variable pulls the name value from the hovered glyph, and @$name fetches the corresponding population data from the ColumnDataSource automatically.
Method 2: Separate HoverTools for Each Series
If you want custom tooltips per country, create individual HoverTools and assign them to specific renderers:
# Create hover tool for Pakistan hover_pak = HoverTool( tooltips=[ ("Year", "@year"), ("Pakistan Population", "@pop_pakistan{0.2f} million") ], renderers=[plot.renderers[0], plot.renderers[1]] # Target Pakistan's line and circle ) # Create hover tool for India hover_ind = HoverTool( tooltips=[ ("Year", "@year"), ("India Population", "@pop_india{0.2f} million") ], renderers=[plot.renderers[2], plot.renderers[3]] # Target India's line and circle ) plot.add_tools(hover_pak, hover_ind)
Just double-check that the renderers list targets the correct glyphs (order matches the sequence you added them to the plot).
3. HoverTool for Multiple Y-Axes
If you’re using multiple Y axes (e.g., to handle different population scales for each country), the setup is similar—you just need to:
- Add a second Y axis to your figure
- Assign each glyph to its respective Y axis with
y_range_name - Ensure your ColumnDataSource includes all necessary data columns
- Configure the HoverTool as normal (it will still pull values directly from the source)
Here’s a quick example:
# Create figure with two Y axes plot = figure( title='Population Graph of India and Pakistan', x_axis_label='Year', y_axis_label='Pakistan Population (million)', y_range=(40, 180) ) # Add second Y axis for India plot.extra_y_ranges = {"india": Range1d(start=400, end=1400)} plot.add_layout(LinearAxis(y_range_name="india", axis_label="India Population (million)"), 'right') # Shared data source (same as before) source = ColumnDataSource(data=dict( year=year, pop_pakistan=pop_pakistan, pop_india=pop_india )) # Add Pakistan's glyphs (uses default Y axis) plot.line('year', 'pop_pakistan', line_width=2, line_color='green', legend='Pakistan', source=source, name='Pakistan') plot.circle('year', 'pop_pakistan', fill_color="green", line_color='green', size=8, source=source, name='Pakistan') # Add India's glyphs (assign to second Y axis) plot.line('year', 'pop_india', line_width=2, line_color='orange', legend='India', source=source, name='India', y_range_name="india") plot.circle('year', 'pop_india', fill_color="orange", line_color='orange', size=8, source=source, name='India', y_range_name="india") # Reuse the same HoverTool from Method 1 hover = HoverTool( tooltips=[ ("Year", "@year"), ("Country", "$name"), ("Population (million)", "@$name{0.2f}") ], mode='vline' ) plot.add_tools(hover)
Full Working Code
Putting it all together, here’s the complete fixed code for a single Y axis:
from bokeh.models import HoverTool, ColumnDataSource from bokeh.plotting import figure, output_file, show if __name__ == '__main__': year = [1960, 1970, 1980, 1990, 2000, 2010] pop_pakistan = [44.91, 58.09, 78.07, 107.7, 138.5, 170.6] pop_india = [449.48, 553.57, 696.783, 870.133, 1000.4, 1309.1] output_file('line.html', mode='inline') plot = figure(title='Population Graph of India and Pakistan', x_axis_label='Year', y_axis_label='Population in million') # Correct ColumnDataSource setup source = ColumnDataSource(data=dict( year=year, pop_pakistan=pop_pakistan, pop_india=pop_india )) # Configure dynamic HoverTool hover = HoverTool( tooltips=[ ("Year", "@year"), ("Country", "$name"), ("Population", "@$name{0.2f} million") ], mode='vline' ) plot.add_tools(hover) # Add glyphs with matching names plot.line('year', 'pop_pakistan', line_width=2, line_color='green', legend='Pakistan', source=source, name='Pakistan') plot.circle('year', 'pop_pakistan', fill_color="green", line_color='green', size=8, source=source, name='Pakistan') plot.line('year', 'pop_india', line_width=2, line_color='orange', legend='India', source=source, name='India') plot.circle('year', 'pop_india', fill_color="orange", line_color='orange', size=8, source=source, name='India') # Optional: Adjust legend position plot.legend.location = 'top_left' show(plot)
内容的提问来源于stack exchange,提问作者Volatil3

