问题
I am using a selection dropdown in Bokeh to change what the chart shows. I'd also like the chart's legend to change accordingly. However, what happens instead is the legend becomes augmented every time the user selects a new dropdown value. Eventually, the legend displays every value from every possible dropdown option simultaneously. How do I "clear" the old legend on every new dropdown select?
import numpy as np
import pandas as pd
import os
from bokeh.plotting import figure
from bokeh.io import output_file, show, save, curdoc
from bokeh.models import HoverTool, CategoricalColorMapper, BoxSelectTool
from bokeh.models import ColumnDataSource, Select, Range1d
from bokeh.layouts import row, column, gridplot
from bokeh.models.widgets import Panel, Tabs
from bokeh.transform import factor_cmap
team = ['Bears', 'Bears', 'Bears', 'Bears', 'Bears']
player = ['Charles Leno', 'James Daniels', 'Cody Whitehair', 'Kyle Long', 'Bobby Massie']
position = ['LT', 'LG', 'C', 'RG', 'RT']
year_acquired = [2014, 2018, 2016, 2013, 2016]
round_drafted = [7, 2, 2, 1, np.NaN]
star=[False, False, False, True, False]
position_loc_x = [-4, -2, 0, 2, 4]
position_loc_y = [0, 0, 0, 0, 0]
year_acquired_color = ['green', 'yellow', 'blue', 'purple', 'blue']
round_drafted_color = ['grey', 'green', 'green', 'purple', np.NaN]
star_color = ['black', 'black', 'black', 'red', 'black']
df = pd.DataFrame({'team':team, 'player':player, 'position':position, 'year_acquired':year_acquired,
'round_drafted':round_drafted, 'star':star, 'position_loc_x':position_loc_x,
'position_loc_y':position_loc_y, 'year_acquired_color':year_acquired_color,
'round_drafted_color':round_drafted_color, 'star_color':star_color})
p = figure(x_range=[-5,5], y_range=[-5, 5])
source = ColumnDataSource(data=df)
p.rect(source=source, x='position_loc_x', y=0, width=1, height=1, line_width=5, line_color=None,
fill_color='round_drafted_color', legend='round_drafted')
hover = HoverTool(tooltips=[('Name', '@player'), ('Round', '@round_drafted'), ('Year', '@year_acquired')])
p.add_tools(hover)
def update():
p.rect(source=source, x='position_loc_x', y=0, width=1, height=1, line_width=5, line_color=None,
fill_color=select.value, legend=select.value.rpartition('_')[0])
select = Select(title='choose an attribute', options=['round_drafted_color', 'year_acquired_color', 'star_color'],
value = 'round_drafted_color')
select.on_change('value', lambda attr, old, new: update())
p.legend.click_policy="hide"
layout = column(select, p)
curdoc().add_root(layout)
回答1:
Instead of plotting the rectangle again with a new color/legendname, you should update the ColumnDataSource used to plot the rectangles. You can do this by editing the source.data
dictionary.
#!/usr/bin/python3
import numpy as np
import pandas as pd
import os
from bokeh.plotting import figure
from bokeh.io import output_file, show, save, curdoc
from bokeh.models import HoverTool, CategoricalColorMapper, BoxSelectTool
from bokeh.models import ColumnDataSource, Select, Range1d
from bokeh.layouts import row, column, gridplot
from bokeh.models.widgets import Panel, Tabs
from bokeh.transform import factor_cmap
team = ['Bears', 'Bears', 'Bears', 'Bears', 'Bears']
player = ['Charles Leno', 'James Daniels', 'Cody Whitehair', 'Kyle Long', 'Bobby Massie']
position = ['LT', 'LG', 'C', 'RG', 'RT']
year_acquired = [2014, 2018, 2016, 2013, 2016]
round_drafted = [7, 2, 2, 1, np.NaN]
star=[False, False, False, True, False]
position_loc_x = [-4, -2, 0, 2, 4]
position_loc_y = [0, 0, 0, 0, 0]
year_acquired_color = ['green', 'yellow', 'blue', 'purple', 'blue']
round_drafted_color = ['grey', 'green', 'green', 'purple', np.NaN]
star_color = ['black', 'black', 'black', 'red', 'black']
df = pd.DataFrame({'team':team, 'player':player, 'position':position, 'year_acquired':year_acquired,
'round_drafted':round_drafted, 'star':star, 'position_loc_x':position_loc_x,
'position_loc_y':position_loc_y, 'year_acquired_color':year_acquired_color,
'round_drafted_color':round_drafted_color, 'star_color':star_color, 'legend':round_drafted, 'color':round_drafted_color})
p = figure(x_range=[-5,5], y_range=[-5, 5])
source = ColumnDataSource(data=df)
glyph = p.rect(source=source, x='position_loc_x', y=0, width=1, height=1, line_width=5, line_color=None,
fill_color='color', legend='legend')
hover = HoverTool(tooltips=[('Name', '@player'), ('Round', '@round_drafted'), ('Year', '@year_acquired')])
p.add_tools(hover)
def update():
#Use select.value to get the right key from the dictionary and set its list as color/legend item
source.data['color'] = source.data[select.value]
source.data['legend'] = source.data[select.value.replace('_color', '')]
select = Select(title='choose an attribute', options=['round_drafted_color', 'year_acquired_color', 'star_color'],
value = 'round_drafted_color')
select.on_change('value', lambda attr, old, new: update())
p.legend.click_policy="hide"
layout = column(select, p)
curdoc().add_root(layout)
来源:https://stackoverflow.com/questions/54273406/how-to-replace-the-legend-in-interactive-bokeh-graph-rather-than-augment-the-le