如何将Matplotlib分配的颜色传入Bokeh自定义图例?
解决Bokeh自定义图例匹配Matplotlib颜色的问题
我来帮你搞定这个颜色同步的问题!你现在的核心痛点是把Matplotlib分配的颜色对应到Bokeh的自定义图例中,因为没使用ColumnDataSource,得手动完成这个关联,下面一步步拆解:
第一步:把Matplotlib颜色转换成Bokeh兼容格式
Matplotlib输出的颜色通常是RGB/RGBA数组(比如(0.3, 0.6, 0.8)),但Bokeh更认十六进制字符串(比如#4d99cc)。我们可以用Matplotlib自带的工具函数转换:
import matplotlib.colors # 假设你已经有Matplotlib分配好的颜色(比如来自colormap或手动指定) mpl_color = (0.3, 0.6, 0.8) bokeh_color = matplotlib.colors.to_hex(mpl_color)
如果是给多个类别分配颜色(比如用colormap批量取色),可以先建立类别和颜色的映射字典:
import matplotlib.pyplot as plt categories = ["类别A", "类别B", "类别C"] # 选一个你用的colormap,这里用viridis举例 cmap = plt.cm.get_cmap("viridis", len(categories)) # 生成类别-十六进制颜色的映射 category_color_map = { cat: matplotlib.colors.to_hex(cmap(i)) for i, cat in enumerate(categories) }
第二步:关联散点渲染器与图例项
因为你没用到ColumnDataSource,应该是按类别逐个绘制散点的。那在绘制每个类别的散点时,要保存对应的渲染器,然后把它和类别、颜色绑定到图例项中:
from bokeh.plotting import figure, show from bokeh.models import Legend, LegendItem # 创建画布 p = figure(title="带自定义图例的散点图", x_axis_label="X轴", y_axis_label="Y轴") legend_items = [] # 假设你的数据按类别分组,比如grouped_data是{类别: (x数据, y数据)} for cat, (x_data, y_data) in grouped_data.items(): # 获取该类别的Bokeh格式颜色 color = category_color_map[cat] # 绘制散点,得到这个类别的渲染器 renderer = p.scatter(x_data, y_data, color=color, size=8, alpha=0.7) # 创建图例项,把渲染器和类别绑定 legend_items.append(LegendItem(label=cat, renderers=[renderer])) # 创建自定义图例并添加到画布 custom_legend = Legend(items=legend_items, location="top_right") p.add_layout(custom_legend) show(p)
如果是Matplotlib自动给散点分配颜色(比如用plt.scatter的c参数)
如果你是通过Matplotlib的散点函数自动给每个点分配颜色(比如基于类别编码),那得先提取每个类别的对应颜色,再同步到Bokeh:
import numpy as np from sklearn.preprocessing import LabelEncoder # 示例数据 x = np.random.rand(100) y = np.random.rand(100) categories = np.random.choice(["类别A", "类别B", "类别C"], 100) # 把类别转换成数值(方便Matplotlib取色) le = LabelEncoder() cat_numeric = le.fit_transform(categories) # Matplotlib生成颜色 cmap = plt.cm.viridis norm = plt.Normalize(cat_numeric.min(), cat_numeric.max()) mpl_colors = cmap(norm(cat_numeric)) # 转换为Bokeh兼容的十六进制颜色 bokeh_colors = [matplotlib.colors.to_hex(c) for c in mpl_colors] # 建立类别-颜色映射(取每个类别第一个点的颜色即可,假设同类别颜色一致) category_color_map = {} for cat, color in zip(categories, bokeh_colors): if cat not in category_color_map: category_color_map[cat] = color # 接下来就可以按上面的方法绘制散点和图例了
补充:HoverTool的小建议
虽然你现在没用到ColumnDataSource,但如果要给HoverTool加类别信息,用ColumnDataSource会更省心。比如把每个类别的数据封装成数据源,HoverTool可以直接读取字段:
from bokeh.models import ColumnDataSource, HoverTool hover = HoverTool(tooltips=[ ("类别", "@category"), ("X值", "@x"), ("Y值", "@y") ]) p = figure(tools=[hover]) legend_items = [] for cat in category_color_map.keys(): # 筛选该类别的数据 mask = categories == cat source = ColumnDataSource(data={ "x": x[mask], "y": y[mask], "category": [cat] * len(x[mask]) }) renderer = p.scatter("x", "y", source=source, color=category_color_map[cat], size=8) legend_items.append(LegendItem(label=cat, renderers=[renderer])) custom_legend = Legend(items=legend_items, location="top_right") p.add_layout(custom_legend) show(p)
这样不仅HoverTool能正常工作,后续的轴切换、年份滑块这类复杂交互,用ColumnDataSource也更容易维护哦!
内容的提问来源于stack exchange,提问作者machump
相关产品推荐
相关产品推荐

