ネットワーク図を可視化する

Bokehは、エッジとノードとの間の構成可能な相互作用を有するネットワークマップ可視化を作成するためのローカルサポートを増加させる。

エッジとノードレンダラー

主な機能は GraphRenderer これは,グラフノードとグラフエッジのための単独の子GlyphRendererを保持する.これはGraphRenenderの node_renderer 財産です。デフォルトのCircleノードフラグ記号、例えば、RECTまたはEllipseフラグ記号のようなデフォルトのCircleノードフラグ記号は、任意のXYGlyphインスタンスで置き換えることができる。同様に,辺のパターン属性も通過することができる edge_renderer 財産です。エッジフラグシンボルは、現在、複数行のフラグシンボルに限定されている。

これらのサブレンダラに属するデータソースにはいくつかの要求があります:

  • 節レンダラに関連するColumnDataSourceは名前の列を持たなければならない. "index" これはノードの一意のインデックスを含む.

  • エッジサブレンダラに関連するColumnDataSourceには2つの必要な列がある: "start" そして "end" それがそうです。これらの列は辺の始点と終点のノードインデックスを含む.

これらのデータソースに追加のメタデータを追加して、ベクトル化された字形パターンを追加するために、またはデータをコールバックまたはホバリングツール提示のために使用することができるようにすることができる。

以下は、以下のコードセグメントである。

  • ノードラベルを楕円に置き換える

  • 設ける height そして width スカラー値である楕円の属性

  • 設ける 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)

上のコードセグメントを実行すると、2 D空間に図形をどのように配置するかは指定されていないので、グラフィックは何も表示されません。あなたは次の節でこれをする方法を知ることができる。

レイアウト提供プログラム

Bokehは単独のものを使用します LayoutProvider モデルは,デカルト空間において図形の座標を提供する.現在内蔵されている唯一の提供プログラムは StaticLayoutProvider モデルは,ノードの(x,y)座標辞書を含む.

この例は、上記のコードセグメントにプログラムを追加する。

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 それがそうです。♪the StaticLayoutProvider あるだろう "xs" そして "ys" データソース列。これらの経路の順序は "start" そして "end" ポイント。なお,それらがノード位置と一致するかどうかは検証されていないため,明示的な経路を設定する際には特に注意が必要である.

この例は、ノード間で二次Bezier経路を描画するために、上記の例を拡張する。

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)

Networkx統合

Bokehは、そのnetworkx統合を介して高速にネットワークマップを描画することをサポートしています。♪the bokeh.plotting.from_networkx 利便性受容 networkx.Graph オブジェクトとnetworkxレイアウト方法は,配置されたGraphRenenderのインスタンスを返す.

次は使用します networkx.spring_layout Networkx内蔵の“Zacharyの空手クラブ図”データセットのレイアウト方法:

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)

相互作用政策

GraphRenenderを設置することで selection_policy そして inspection_policy 属性です。これらの戦略属性は特殊な GraphHitTestPolicy モデル例。

例えば、設定 selection_policy=NodesAndLinkedEdges() 選定されたノードにも関連するエッジを選択させる.同様に設定します inspection_policy=EdgesAndLinkedNodes() エッジの開始ノードと終了ノードをホバリングツールを用いてエッジをホバリングする際にも検査される.

Users may want to customize the selection_glyph, nonselection_glyph, and/or hover_glyph attributes of the edge and node sub-renderers in order to add dynamic visual elements to their graph interactions.

以下は、ノードとエッジの相互作用を追加したグラフの例である。

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 それがそうです。

たとえば,“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)