Bokeh增加了对创建网络图可视化的本地支持,在边和节点之间可以配置交互。
的主要功能 GraphRenderer RER为渲染节点维护单独的边。这允许通过修改GraphRenderer的 node_renderer 财产。可以将默认的圆节点图示符替换为任何XYGlyph实例,例如矩形或椭圆glyph。类似地,可以通过 edge_renderer 财产。边缘图示符当前仅限于多行图示符。
GraphRenderer
node_renderer
edge_renderer
对于属于这些子渲染器的数据源有两个要求:
与节点子呈现器关联的ColumnDataSource必须具有名为 "index" 包含节点的唯一索引。
"index"
所需的子列边缘呈现器与两个列关联: "start" 和 "end" . 这些列包含边的起点和终点的节点索引。
"start"
"end"
可以将额外的元数据添加到这些数据源中,以便添加矢量化字形样式或使数据可用于回调或悬停工具提示。
下面是一个代码片段:
将节点图示符替换为椭圆
设置 height 和 width 椭圆的属性作为标量值
height
width
设置 fill_color 属性将椭圆作为矢量化字段,并将值添加到节点数据源。
fill_color
import math from bokeh.plotting import figure from bokeh.models import GraphRenderer, Ellipse from bokeh.palettes import Spectral8 N = 8 node_indices = list(range(N)) plot = figure(title="Graph Layout Demonstration", x_range=(-1.1,1.1), y_range=(-1.1,1.1), tools="", toolbar_location=None) graph = GraphRenderer() graph.node_renderer.glyph = Ellipse(height=0.1, width=0.2, fill_color="fill_color") graph.node_renderer.data_source.data = dict( index=node_indices, fill_color=Spectral8) graph.edge_renderer.data_source.data = dict( start=[0]*N, end=node_indices)
运行上述代码段不会呈现任何图形,因为我们还没有指定如何在二维空间中排列图形。您可以在下面的部分学习如何做到这一点。
Bokeh使用单独的 LayoutProvider 为提供笛卡尔空间中的图形坐标而建立的模型。目前,唯一的内置提供程序是 StaticLayoutProvider 字典包含模型的坐标。
LayoutProvider
StaticLayoutProvider
此示例将提供程序添加到上面的代码段:
import math from bokeh.io import output_file, show from bokeh.models import Ellipse, GraphRenderer, StaticLayoutProvider from bokeh.palettes import Spectral8 from bokeh.plotting import figure N = 8 node_indices = list(range(N)) plot = figure(title='Graph Layout Demonstration', x_range=(-1.1,1.1), y_range=(-1.1,1.1), tools='', toolbar_location=None) graph = GraphRenderer() graph.node_renderer.data_source.add(node_indices, 'index') graph.node_renderer.data_source.add(Spectral8, 'color') graph.node_renderer.glyph = Ellipse(height=0.1, width=0.2, fill_color='color') graph.edge_renderer.data_source.data = dict( start=[0]*N, end=node_indices) ### start of layout code circ = [i*2*math.pi/8 for i in node_indices] x = [math.cos(i) for i in circ] y = [math.sin(i) for i in circ] graph_layout = dict(zip(node_indices, zip(x, y))) graph.layout_provider = StaticLayoutProvider(graph_layout=graph_layout) plot.renderers.append(graph) output_file('graph.html') show(plot)
默认情况下, StaticLayoutProvider 将在提供的节点位置之间绘制直线路径。为了提供显式的边路径,还可以提供 edge_renderer bokeh.models.sources.ColumnDataSource . 这个 StaticLayoutProvider 将在 "xs" 和 "ys" 数据源的列。请注意,这些路径的顺序应与 "start" 和 "end" 积分。还要注意,没有验证它们是否与节点位置匹配,所以在设置显式路径时要格外小心。
bokeh.models.sources.ColumnDataSource
"xs"
"ys"
此示例扩展了上述示例,以在节点之间绘制二次贝塞尔路径:
import math from bokeh.io import output_file, show from bokeh.models import Ellipse, GraphRenderer, StaticLayoutProvider from bokeh.palettes import Spectral8 from bokeh.plotting import figure N = 8 node_indices = list(range(N)) plot = figure(title="Graph Layout Demonstration", x_range=(-1.1,1.1), y_range=(-1.1,1.1), tools="", toolbar_location=None) graph = GraphRenderer() graph.node_renderer.data_source.add(node_indices, 'index') graph.node_renderer.data_source.add(Spectral8, 'color') graph.node_renderer.glyph = Ellipse(height=0.1, width=0.2, fill_color="color") graph.edge_renderer.data_source.data = dict( start=[0]*N, end=node_indices) ### start of layout code circ = [i*2*math.pi/8 for i in node_indices] x = [math.cos(i) for i in circ] y = [math.sin(i) for i in circ] graph_layout = dict(zip(node_indices, zip(x, y))) graph.layout_provider = StaticLayoutProvider(graph_layout=graph_layout) ### Draw quadratic bezier paths def bezier(start, end, control, steps): return [(1-s)**2*start + 2*(1-s)*s*control + s**2*end for s in steps] xs, ys = [], [] sx, sy = graph_layout[0] steps = [i/100. for i in range(100)] for node_index in node_indices: ex, ey = graph_layout[node_index] xs.append(bezier(sx, ex, 0, steps)) ys.append(bezier(sy, ey, 0, steps)) graph.edge_renderer.data_source.data['xs'] = xs graph.edge_renderer.data_source.data['ys'] = ys plot.renderers.append(graph) output_file("graph.html") show(plot)
Bokeh支持用networkx集成快速绘制网络图。这个 bokeh.plotting.from_networkx 便利方法接受 networkx.Graph 对象和networkx布局方法,以返回已配置的GraphRender实例。
bokeh.plotting.from_networkx
networkx.Graph
下面是一个使用 networkx.spring_layout networkx内置“Zachary的空手道俱乐部图”数据集的布局方法:
networkx.spring_layout
import networkx as nx from bokeh.io import output_file, show from bokeh.plotting import figure, from_networkx G = nx.karate_club_graph() plot = figure(title="Networkx Integration Demonstration", x_range=(-1.1,1.1), y_range=(-1.1,1.1), tools="", toolbar_location=None) graph = from_networkx(G, nx.spring_layout, scale=2, center=(0,0)) plot.renderers.append(graph) output_file("networkx_graph.html") show(plot)
可以通过设置GraphRenderer的 selection_policy 和 inspection_policy 属性。这些策略属性接受一个特殊的 GraphHitTestPolicy 模型实例。
selection_policy
inspection_policy
GraphHitTestPolicy
例如,设置 selection_policy=NodesAndLinkedEdges() 将导致选定节点也选择关联的边。同样,设置 inspection_policy=EdgesAndLinkedNodes() 将导致在使用悬停工具悬停边时检查边的起点和终点节点。
selection_policy=NodesAndLinkedEdges()
inspection_policy=EdgesAndLinkedNodes()
用户可能希望自定义 selection_glyph , nonselection_glyph 和/或 hover_glyph 边和节点子渲染器的属性,以便将动态可视元素添加到它们的图形交互中。
selection_glyph
nonselection_glyph
hover_glyph
下面是一个添加了节点和边交互的图形示例:
import networkx as nx from bokeh.io import output_file, show from bokeh.models import (BoxSelectTool, Circle, EdgesAndLinkedNodes, HoverTool, MultiLine, NodesAndLinkedEdges, Plot, Range1d, TapTool,) from bokeh.palettes import Spectral4 from bokeh.plotting import from_networkx G=nx.karate_club_graph() plot = Plot(plot_width=400, plot_height=400, x_range=Range1d(-1.1,1.1), y_range=Range1d(-1.1,1.1)) plot.title.text = "Graph Interaction Demonstration" plot.add_tools(HoverTool(tooltips=None), TapTool(), BoxSelectTool()) graph_renderer = from_networkx(G, nx.circular_layout, scale=1, center=(0,0)) graph_renderer.node_renderer.glyph = Circle(size=15, fill_color=Spectral4[0]) graph_renderer.node_renderer.selection_glyph = Circle(size=15, fill_color=Spectral4[2]) graph_renderer.node_renderer.hover_glyph = Circle(size=15, fill_color=Spectral4[1]) graph_renderer.edge_renderer.glyph = MultiLine(line_color="#CCCCCC", line_alpha=0.8, line_width=5) graph_renderer.edge_renderer.selection_glyph = MultiLine(line_color=Spectral4[2], line_width=5) graph_renderer.edge_renderer.hover_glyph = MultiLine(line_color=Spectral4[1], line_width=5) graph_renderer.selection_policy = NodesAndLinkedEdges() graph_renderer.inspection_policy = EdgesAndLinkedNodes() plot.renderers.append(graph_renderer) output_file("interactive_graphs.html") show(plot)
在 from_networkx, NetworkX's node/edge attributes are converted for GraphRenderer's node_renderer/edge_renderer .
from_networkx
例如,“Zachary的空手道俱乐部图”数据集有一个名为“Club”的节点属性。可以使用在中转换的节点属性悬停此信息 from_networkx . 类似地,节点/边属性也可以用于颜色信息。
以下是一个图形示例,它悬停节点属性并使用边属性更改颜色:
import networkx as nx from bokeh.io import output_file, show from bokeh.models import (BoxZoomTool, Circle, HoverTool, MultiLine, Plot, Range1d, ResetTool,) from bokeh.palettes import Spectral4 from bokeh.plotting import from_networkx # Prepare Data G = nx.karate_club_graph() SAME_CLUB_COLOR, DIFFERENT_CLUB_COLOR = "black", "red" edge_attrs = {} for start_node, end_node, _ in G.edges(data=True): edge_color = SAME_CLUB_COLOR if G.nodes[start_node]["club"] == G.nodes[end_node]["club"] else DIFFERENT_CLUB_COLOR edge_attrs[(start_node, end_node)] = edge_color nx.set_edge_attributes(G, edge_attrs, "edge_color") # Show with Bokeh plot = Plot(plot_width=400, plot_height=400, x_range=Range1d(-1.1, 1.1), y_range=Range1d(-1.1, 1.1)) plot.title.text = "Graph Interaction Demonstration" node_hover_tool = HoverTool(tooltips=[("index", "@index"), ("club", "@club")]) plot.add_tools(node_hover_tool, BoxZoomTool(), ResetTool()) graph_renderer = from_networkx(G, nx.spring_layout, scale=1, center=(0, 0)) graph_renderer.node_renderer.glyph = Circle(size=15, fill_color=Spectral4[0]) graph_renderer.edge_renderer.glyph = MultiLine(line_color="edge_color", line_alpha=0.8, line_width=1) plot.renderers.append(graph_renderer) output_file("interactive_graphs.html") show(plot)