如何将Holoviews图转为Bokeh GraphRenderer,兼顾箭头与节点样式控制?
解决Holoviews有向图+Bokeh节点属性控制的兼容问题
我明白你的痛点——想同时用上Holoviews的有向图箭头效果,又想用Bokeh的GraphRenderer来精细控制节点的颜色、大小和交互状态,但Holoviews渲染后返回的是Figure对象,没法直接调用node_renderer对吧?这里有两种靠谱的解决办法:
方法一:直接用Holoviews的opts完成所有配置(推荐)
其实Holoviews的Graph对象本身就支持绑定节点属性来控制样式,完全不需要切换到Bokeh的底层API。这样既可以保留directed=True的箭头,又能实现你要的基于属性的节点定制:
import networkx as nx import holoviews as hv from holoviews import opts hv.extension('bokeh') from bokeh.io import show, output_file from bokeh.palettes import Viridis256, Inferno6 # 构建有向图 edgeList = [("A", "B"), ("B", "C"), ("A", "C"), ("C", "D"), ("D", "A")] G = nx.DiGraph(edgeList) # 给节点添加属性 numConnections = {k:v for k,v in G.out_degree()} nx.set_node_attributes(G, numConnections, name='numConnections') # 模拟节点大小属性(根据连接数调整) node_sizes = {k: v*10 for k, v in numConnections.items()} nx.set_node_attributes(G, node_sizes, name='node_size') # 创建Holoviews Graph并一次性配置所有样式 hvGraph = hv.Graph.from_networkx(G, nx.spring_layout).opts( tools=['hover', 'tap', 'box_select'], directed=True, arrowhead_length=0.01, # 绑定节点属性到大小和颜色 node_size='node_size', node_color='numConnections', node_cmap=Viridis256, node_fill_alpha=0.8, # 配置选中/悬停时的节点样式 selection_fill_color=Inferno6[4], selection_node_size=25, hover_fill_color=Inferno6[3], hover_node_size=20, # 配置边的基础/选中/悬停样式 edge_color="#CCCCCC", edge_alpha=0.8, edge_line_width=3, selection_edge_color=Inferno6[4], selection_edge_line_width=4, hover_edge_color=Inferno6[3], hover_edge_line_width=4, # 添加颜色条 colorbar=True, colorbar_title='#connections' ) # 渲染并导出 hvRendered = hv.render(hvGraph, 'bokeh') output_file("holoviews_bokeh_combined.html") show(hvRendered)
这种方式用Holoviews的声明式语法就能搞定一切,代码更简洁,也不用在两个库的API之间切换。
方法二:从Holoviews渲染的Figure中提取GraphRenderer
如果你一定要用Bokeh的GraphRenderer API来做更精细的控制,可以遍历渲染后Figure的renderers列表,找到其中的GraphRenderer实例,之后就可以像纯Bokeh代码一样操作它:
import networkx as nx import holoviews as hv from holoviews import opts hv.extension('bokeh') from bokeh.io import show, output_file from bokeh.models import LinearColorMapper, Circle, MultiLine from bokeh.palettes import Viridis256, Inferno6 from bokeh.models.graphs import NodesAndLinkedEdges # 构建有向图和属性(同方法一) edgeList = [("A", "B"), ("B", "C"), ("A", "C"), ("C", "D"), ("D", "A")] G = nx.DiGraph(edgeList) numConnections = {k:v for k,v in G.out_degree()} nx.set_node_attributes(G, numConnections, name='numConnections') node_sizes = {k: v*10 for k, v in numConnections.items()} nx.set_node_attributes(G, node_sizes, name='node_size') # 创建带箭头的Holoviews有向图 hvGraph = hv.Graph.from_networkx(G, nx.spring_layout).opts( tools=['hover'], directed=True, arrowhead_length=0.01 ) # 渲染为Bokeh Figure hvRendered = hv.render(hvGraph, 'bokeh') # 从Figure的渲染器中找到GraphRenderer graph_renderer = None for renderer in hvRendered.renderers: if hasattr(renderer, 'node_renderer'): graph_renderer = renderer break # 现在可以像纯Bokeh代码一样配置样式了 if graph_renderer: # 配置颜色映射 mapper = LinearColorMapper( palette="Viridis256", low=min(numConnections.values()), high=max(numConnections.values()) ) # 节点样式配置 graph_renderer.node_renderer.glyph = Circle( size='node_size', fill_color={'field': "numConnections", "transform": mapper}, fill_alpha=.8 ) graph_renderer.node_renderer.selection_glyph = Circle( size=25, fill_color=Inferno6[4] ) graph_renderer.node_renderer.hover_glyph = Circle( size=20, fill_color=Inferno6[3] ) # 边样式配置 graph_renderer.edge_renderer.glyph = MultiLine( line_color="#CCCCCC", line_alpha=0.8, line_width=3 ) graph_renderer.edge_renderer.selection_glyph = MultiLine( line_color=Inferno6[4], line_width=4 ) graph_renderer.edge_renderer.hover_glyph = MultiLine( line_color=Inferno6[3], line_width=4 ) # 配置选择和检查策略 graph_renderer.selection_policy = NodesAndLinkedEdges() graph_renderer.inspection_policy = NodesAndLinkedEdges() # 添加颜色条 from bokeh.models import ColorBar, BasicTicker bar = ColorBar(color_mapper=mapper, location=(0,0), title='#connections') hvRendered.add_layout(bar, 'right') # 保存并显示 output_file("holoviews_extract_graphrenderer.html") show(hvRendered)
为什么之前会报错?
你遇到的AttributeError: 'BokehRenderer' object has no attribute 'node_renderer',是因为hv.render()返回的是Bokeh的Figure对象,而node_renderer是GraphRenderer独有的属性。所以必须先从Figure的renderers集合里找到对应的GraphRenderer实例,才能访问这些属性。
内容的提问来源于stack exchange,提问作者albertk
相关产品推荐
相关产品推荐

