Python
を使用して線形ネットワークグラフを作成しようとしています(matplotlib
とnetworkx
を使用するのが望ましいですが、bokeh
)以下の概念と同様の概念です。
このグラフプロットを効率的に構築するにはどうすればよいですか(pos
?)in Python using networkx
?これをより複雑な例に使用するので、この単純な例の位置をハードコーディングすることは役に立たないと思います:(。networkx
にはこれに対する解決策がありますか?
networkx
でこれを実現する方法についてのチュートリアルは見たことがありません。そのため、この質問はコミュニティにとって信頼できるリソースになると思います。 networkx
チュートリアル を広範囲にわたって調べましたが、このようなものはありません。 networkx
のレイアウトでは、pos
引数を注意深く使用しないとこのタイプのネットワークを解釈できなくなります...これが私の唯一の選択肢だと思います。 https://networkx.github.io/documentation/networkx-1.9/reference/drawing.html ドキュメントの事前計算されたレイアウトはどれもこのタイプのネットワークを処理していないようですよく構造化されています。
簡単な例:
(A)すべての外側のキーは、グラフ内で左から右に移動する反復です(たとえば、反復0はサンプルを表し、反復1にはグループ1〜3があり、反復2と同じように、反復3にはグループ1〜2があります)。 (B)内部ディクショナリには、その特定の反復での現在のグループ化と、現在のグループを表すマージされた前のグループの重みが含まれます(たとえば、_iteration 3
_には_Group 1
_と_Group 2
_があり、_iteration 4
_すべての_iteration 3's
_ _Group 2
_は_iteration 4's
_ _Group 2
_になりましたが、_iteration 3's
_ _Group 1
_は分割されています。 1.1。
上記のプロットの重み付き接続の私のコード:
_D_iter_current_previous = {
1: {
"Group 1":{"sample_0":0.5, "sample_1":0.5, "sample_2":0, "sample_3":0, "sample_4":0},
"Group 2":{"sample_0":0, "sample_1":0, "sample_2":1, "sample_3":0, "sample_4":0},
"Group 3":{"sample_0":0, "sample_1":0, "sample_2":0, "sample_3":0.5, "sample_4":0.5}
},
2: {
"Group 1":{"Group 1":1, "Group 2":0, "Group 3":0},
"Group 2":{"Group 1":0, "Group 2":1, "Group 3":0},
"Group 3":{"Group 1":0, "Group 2":0, "Group 3":1}
},
3: {
"Group 1":{"Group 1":0.25, "Group 2":0, "Group 3":0.75},
"Group 2":{"Group 1":0.25, "Group 2":0.75, "Group 3":0}
},
4: {
"Group 1":{"Group 1":1, "Group 2":0},
"Group 2":{"Group 1":0.25, "Group 2":0.75}
}
}
_
これは、networkx
でグラフを作成したときに起こったことです。
_import networkx
import matplotlib.pyplot as plt
# Create Directed Graph
G = nx.DiGraph()
# Iterate through all connections
for iter_n, D_current_previous in D_iter_current_previous.items():
for current_group, D_previous_weights in D_current_previous.items():
for previous_group, weight in D_previous_weights.items():
if weight > 0:
# Define connections using `|__|` as a delimiter for the names
previous_node = "%d|__|%s"%(iter_n - 1, previous_group)
current_node = "%d|__|%s"%(iter_n, current_group)
connection = (previous_node, current_node)
G.add_Edge(*connection, weight=weight)
# Draw Graph with labels and width thickness
nx.draw(G, with_labels=True, width=[G[u][v]['weight'] for u,v in G.edges()])
_
注:これを行う他の唯一の方法は、matplotlib
で、反復を表すすべてのティック(初期サンプルを含む5)で散布図を作成し、異なる重みでポイントを相互に接続することです。 。これは、特に接続を使用してマーカーのエッジを整列させようとするかなり厄介なコードになります...しかし、これとnetworkx
がそれを行うための最良の方法であるかどうか、またはそこにあるかどうかはわかりませんは、このタイプのプロット用に設計されたツール(例:bokeh
またはplotly
)です。
Networkxには探索的データ分析のための適切なプロット機能がありますが、ここでは取り上げたくないさまざまな理由から、出版物の品質を数値化するツールではありません。したがって、コードベースのその部分を最初から書き直し、netgraphと呼ばれるスタンドアロンの描画モジュールを作成しました。これは ここ (純粋にmatplotlibに基づくオリジナルのように)です。 APIは非常によく似ており、十分に文書化されているため、目的に合わせて作成するのはそれほど難しくありません。
その上に構築すると、次の結果が得られます。
できるだけエッジの強さを表す色を選びました
1)負の値を示し、
2)小さい値をよりよく区別します。
ただし、代わりにエッジ幅をネットグラフに渡すこともできます(netgraph.draw_edges()
を参照)。
ブランチの順序が異なるのは、データ構造(dict)の結果であり、固有の順序がないことを示しています。この問題を修正するには、データ構造と以下の関数_parse_input()
を修正する必要があります。
コード:
import itertools
import numpy as np
import matplotlib.pyplot as plt
import netgraph; reload(netgraph)
def plot_layered_network(weight_matrices,
distance_between_layers=2,
distance_between_nodes=1,
layer_labels=None,
**kwargs):
"""
Convenience function to plot layered network.
Arguments:
----------
weight_matrices: [w1, w2, ..., wn]
list of weight matrices defining the connectivity between layers;
each weight matrix is a 2-D ndarray with rows indexing source and columns indexing targets;
the number of sources has to match the number of targets in the last layer
distance_between_layers: int
distance_between_nodes: int
layer_labels: [str1, str2, ..., strn+1]
labels of layers
**kwargs: passed to netgraph.draw()
Returns:
--------
ax: matplotlib axis instance
"""
nodes_per_layer = _get_nodes_per_layer(weight_matrices)
node_positions = _get_node_positions(nodes_per_layer,
distance_between_layers,
distance_between_nodes)
w = _combine_weight_matrices(weight_matrices, nodes_per_layer)
ax = netgraph.draw(w, node_positions, **kwargs)
if not layer_labels is None:
ax.set_xticks(distance_between_layers*np.arange(len(weight_matrices)+1))
ax.set_xticklabels(layer_labels)
ax.xaxis.set_ticks_position('bottom')
return ax
def _get_nodes_per_layer(weight_matrices):
nodes_per_layer = []
for w in weight_matrices:
sources, targets = w.shape
nodes_per_layer.append(sources)
nodes_per_layer.append(targets)
return nodes_per_layer
def _get_node_positions(nodes_per_layer,
distance_between_layers,
distance_between_nodes):
x = []
y = []
for ii, n in enumerate(nodes_per_layer):
x.append(distance_between_nodes * np.arange(0., n))
y.append(ii * distance_between_layers * np.ones((n)))
x = np.concatenate(x)
y = np.concatenate(y)
return np.c_[y,x]
def _combine_weight_matrices(weight_matrices, nodes_per_layer):
total_nodes = np.sum(nodes_per_layer)
w = np.full((total_nodes, total_nodes), np.nan, np.float)
a = 0
b = nodes_per_layer[0]
for ii, ww in enumerate(weight_matrices):
w[a:a+ww.shape[0], b:b+ww.shape[1]] = ww
a += nodes_per_layer[ii]
b += nodes_per_layer[ii+1]
return w
def test():
w1 = np.random.Rand(4,5) #< 0.50
w2 = np.random.Rand(5,6) #< 0.25
w3 = np.random.Rand(6,3) #< 0.75
import string
node_labels = dict(Zip(range(18), list(string.ascii_lowercase)))
fig, ax = plt.subplots(1,1)
plot_layered_network([w1,w2,w3],
layer_labels=['start', 'step 1', 'step 2', 'finish'],
ax=ax,
node_size=20,
node_Edge_width=2,
node_labels=node_labels,
Edge_width=5,
)
plt.show()
return
def test_example(input_dict):
weight_matrices, node_labels = _parse_input(input_dict)
fig, ax = plt.subplots(1,1)
plot_layered_network(weight_matrices,
layer_labels=['', '1', '2', '3', '4'],
distance_between_layers=10,
distance_between_nodes=8,
ax=ax,
node_size=300,
node_Edge_width=10,
node_labels=node_labels,
Edge_width=50,
)
plt.show()
return
def _parse_input(input_dict):
weight_matrices = []
node_labels = []
# initialise sources
sources = set()
for v in input_dict[1].values():
for s in v.keys():
sources.add(s)
sources = list(sources)
for ii in range(len(input_dict)):
inner_dict = input_dict[ii+1]
targets = inner_dict.keys()
w = np.full((len(sources), len(targets)), np.nan, np.float)
for ii, s in enumerate(sources):
for jj, t in enumerate(targets):
try:
w[ii,jj] = inner_dict[t][s]
except KeyError:
pass
weight_matrices.append(w)
node_labels.append(sources)
sources = targets
node_labels.append(targets)
node_labels = list(itertools.chain.from_iterable(node_labels))
node_labels = dict(enumerate(node_labels))
return weight_matrices, node_labels
# --------------------------------------------------------------------------------
# script
# --------------------------------------------------------------------------------
if __name__ == "__main__":
# test()
input_dict = {
1: {
"Group 1":{"sample_0":0.5, "sample_1":0.5, "sample_2":0, "sample_3":0, "sample_4":0},
"Group 2":{"sample_0":0, "sample_1":0, "sample_2":1, "sample_3":0, "sample_4":0},
"Group 3":{"sample_0":0, "sample_1":0, "sample_2":0, "sample_3":0.5, "sample_4":0.5}
},
2: {
"Group 1":{"Group 1":1, "Group 2":0, "Group 3":0},
"Group 2":{"Group 1":0, "Group 2":1, "Group 3":0},
"Group 3":{"Group 1":0, "Group 2":0, "Group 3":1}
},
3: {
"Group 1":{"Group 1":0.25, "Group 2":0, "Group 3":0.75},
"Group 2":{"Group 1":0.25, "Group 2":0.75, "Group 3":0}
},
4: {
"Group 1":{"Group 1":1, "Group 2":0},
"Group 2":{"Group 1":0.25, "Group 2":0.75}
}
}
test_example(input_dict)
pass