Pythonには、データ分析をサポートする様々なモジュールが存在します。
その一つに、強力なネットワーク分析ツールとしてNetworkXというものがあります。
NetworkXを使えば、次のようなネットワーク分析を簡単に行うことができます。
- ネットワーク分析
- ネットワーク構造の可視化
- 中心性や連結性などの解析
この記事では、NetworkX
の基本的な使い方を中心にわかりやすく解説していきます。
さらに詳しい内容については、次のような書籍を参考にしてみてください。
- ネットワーク分析の準備
- ネットワーク分析・可視化の超基本形
- 基本的な特性値の出力
- ネットワーク図の見た目変更
- ネットワーク可視化例|次数に応じた配色・サイズ変更
- 最短経路の探索
- ネットワーク分析おススメの1冊!
ネットワーク分析の準備
まずはネットワーク分析の基礎知識、分析のための環境構築について解説していきます。
ちなみに以下で解説するような基本的な内容を網羅的に学びたい場合は次のような書籍がおススメです。
私もこの書籍から入門しています。
ネットワーク分析の基礎知識
ネットワーク分析に関する超基本用語を整理していきましょう。
次の4つの用語をおさえれば、基本的な可視化を行うことができます。
要素 | NetwrokX での名前 |
適用 |
---|---|---|
ネットワーク | Graph |
ネットワーク図全体のこと |
頂点 | node |
一つ一つの要素のこと |
辺 | edge |
頂点と頂点を繋ぐ線のこと |
次数 | degree |
頂点の持つ辺の数のこと |
ネットワーク図を使って、各要素がどこに対応するか確認してみましょう。
この他にも必要に応じて各要素に任意の属性を持たせることも可能です。
例えば、次のような要素を与えることができます。
size
:node
の大きさweight
:node
やedge
の重み(経路探索などの最適化問題で使うことがある)color
:node
やedge
の色
これらは属性として与えられるというだけで、属性をネットワーク図に反映するには別途設定が必要です。
基本用語を把握できたので、次はネットワーク分析用のモジュールNetworkX
について解説します。
NetworkXとは?
NetworkX
とは、Pythonでネットワーク分析を行うため外部モジュールです。
NetworkX
を使うと次の様な操作を行うことができます。
- ネットワーク図の作成
- 各要素のデータの出力
- ネットワークの分析(最適解の探索など)
特にグラフの作成が非常に簡単かつ高速なので、Python
でネットワーク分析を行う際には必須のモジュールとも言えます。
以下でNetworkX
の基本的な使い方をわかりやすく解説していきます。
環境構築
NetworkX
は組み込みライブラリではないので、pip
またはconda
でインストールします。
pip
でインストールする場合は、ターミナルで以下のコマンドを実行します。
pip install pypdf
Anaconda
の場合は次のコマンドでインストールしましょう。
conda install networkx
ただし、conda
の場合はNetworkX
のバージョンが古い場合があります。
最新のNetworkX
に比べて、バージョンが古い場合はconda-forge
からインストールするのも一つの手です。
conda install networkx -c conda-forge
- 【参考】conda-forgeの使い方については、次の記事で詳しく解説しています。
環境構築は飛ばして、とりあえずネットワーク分析を試したい場合は、Google Colaboratory
を使用すれば、初めからインストールされているので設定不要です。
NetworkXのインポート
NetworkX
のインストールが終わったらコード上でインポートして使いましょう。
NetworkX
はnx
という別名でインポートする慣例があるようです。
import networkx as nx
ネットワーク分析ではグラフの出力をすることもあるので、一緒にMaplotlib
もインストールしておくと良いですね。
import matplotlib.pyplot as plt
import networkx as nx
NetworkX
のバージョンは次のスクリプトで確認できます。
import networkx as nx
print(nx.__version__)
# 3.1
NetworkX
とMatplotlib
で、どちらかのバージョンが極端に古いと互換性がない場合があります。
両方とも新しいバージョンにしておくと、そういったトラブルを避けやすくなると思います。
- 【参考】
Matplotlib
の使い方については、次の記事で詳しく解説しています。
ネットワーク分析・可視化の超基本形
ネットワークの可視化のためには、次の手順を踏みます。
Graph
オブジェクトの作成:G = nx.Graph()
node
の追加:G.add_nodes_from([node1, node2, node3,…])
edge
の追加:G.add_edges_from([(node1, node2), (node2, node3), …])
- ネットワークの可視化:
nx.draw(G, witlabels = True)
オブジェクト作成から、可視化までの基本形は次のようになります。
# Graphオブジェクトの作成
G = nx.Graph()
# nodeデータの追加
G.add_nodes_from(["A", "B", "C", "D", "E", "F"])
# edgeデータの追加
G.add_edges_from([("A", "B"), ("B", "C"), ("B", "F"),("C", "D"), ("C", "E"), ("C", "F"), ("B", "F")])
# ネットワークの可視化
nx.draw(G, with_labels = True)
plt.show()
これで、次のようなグラフが描けます。
各手順について詳細を確認していきましょう。
Graphオブジェクトの作成
まずは、Graph
オブジェクトを生成します。
G = nx.Graph()
この時点では、空のグラフが生成されているだけです。
このグラフにnode
やedge
を追加して、ネットワークを完成させていきます。
nodeの追加
次にnode
を追加していきます。
1つだけ追加する場合は、.add_node()
, 2つ以上追加する場合は.add_nodes_from()
を使います。
G.add_node(node1)
G.add_nodes_from([node1, node2, node3,…])
node
の要素としては辞書のキーとして使えるものが使用できると考えると良いです。
G = nx.Graph()
G.add_nodes_from(["A", "B", "C", "D", "E", "F"])
nx.draw(G, with_labels = True)
plt.savefig("03_add_nodes_fromの例.png", bbox_inches="tight")
plt.show()
次のようにnode
が生成されています。
edge
は指定していないので、node
同士は結合していませんね。
次のようにnode
に属性を与えることもできます。
G.add_nodes_from(["A", "B"], size=10)
G.add_nodes_from([("A", {"size":10}), ("B", {"size":20})])
任意の属性を与えることが可能です。
ただし、属性を設定するだけで自動でグラフの見た目が変化するわけではないです。
グラフに反映する場合は自分で見た目の変更を行いましょう。
edgeの追加
次にnode
を追加していきます。
1組だけ追加する場合は、.add_edge()
, 2つ以上追加する場合は.add_edges_from()
を使います。
G.add_edge(node1, node2)
G.add_edges_from([(node1, node2), (node2, node3), …])
存在しないnode
を指定すると自動で追加されます。
## edgeの追加
# ネットワーク可視化の実行例
# Graphオブジェクトの作成
G = nx.Graph()
# nodeデータの追加
G.add_nodes_from(["A", "B", "C", "D", "E", "F"])
# edgeデータの追加
G.add_edges_from([("A", "B"), ("B", "C"), ("B", "F"),("C", "D"), ("C", "E"), ("C", "F"), ("B", "F")])
# ネットワークの可視化
nx.draw(G, with_labels = True)
plt.savefig("04_edgeの追加例.png", bbox_inches="tight")
plt.show()
次のようなネットワーク図が完成します。
node
と同様に、edge
にも属性を与えることができます。
edge
の属性としては、wieght
を与えることが多いので、weight
を与えるためのメソッドも存在します。
G.add_edges_from([("A", "B"), ("B", "C")], weight=1)
G.add_weighted_edges_from([("A", "B", 1), ("B", "C", 2)])
.add_weighted_edges_from()
では、("node1", "node2", weight)
の組み合わせを渡しています。
ネットワーク図の保存
作成した図を保存するのは、Matplotlib
と同様にplt.savefig()
を使用します。
plt.savefig("保存名.png")
【参考】Matplotlib
での画像の保存方法については、次の記事で詳しく解説しています。
基本的な特性値の出力
次に作成したネットワークの基本データを出力する方法を確認していきましょう。
出力内容 | 出力方法 |
---|---|
ノードとエッジの数 | print(G) |
ノードの数 | G.number_of_nodes() |
ノードの要素 | G.nodes() |
エッジの数 | G.number_of_edges() |
エッジの要素 | G.edges() |
各ノードの実数 | G.degrees() |
バージョン2.X
までは、nx.info()
という関数で基本テータを出力できましたが、3.X
からはこの関数はなくなりました。
基本データをさっと確認する場合はprint()
関数で確認します。
print(G)
# Graph with 7 nodes and 6 edges
その他の実行例も確認してみましょう。
ノードの出力
ノードの基本出力メソッドは次の通りです。
- ノードの数:
G.number_of_nodes()
- ノードの要素:
G.nodes()
サンプルコードで挙動を確認してみましょう。
print("number of nodes:",G.number_of_nodes())
print("nodes:", G.nodes())
# number of nodes: 6
# nodes: ['A', 'B', 'C', 'D', 'E', 'F']
隣接したノードの出力方法
隣接したノードを出力したい場合は、.all_neighbors()
関数を使います。
nx.all_neighbors(G, ノード名)
結果はイテレータで返されます。
サンプルコードで挙動を確認してみましょう。
for n in nx.all_neighbors(G, "C"):
print(n)
# B
# D
# E
# F
エッジの出力
エッジの基本出力メソッドは次の通りです。
- エッジの数:
G.number_of_edges()
- エッジの要素:
G.edges()
サンプルコードで挙動を確認してみましょう。
print("number of edges:",G.number_of_edges())
print("edges:", G.edges())
# number of edges: 6
# edges: [('A', 'B'), ('B', 'C'), ('B', 'F'), ('C', 'D'), ('C', 'E'), ('C', 'F')]
次数の出力
次数の基本出力メソッドは次の通りです。
- 各ノードの次数:
G.degrees()
各ノードの次数が自動集計を自動で集計することができます。
サンプルコードで挙動を確認してみましょう。
print("degrees:", G.degree())
# degrees: [('A', 1), ('B', 3), ('C', 4), ('D', 1), ('E', 1), ('F', 2)]
ネットワーク図の見た目変更
ネットワーク図の見た目を変更するには、nx.draw()
メソッドでキーワード引数を設定します。
基本的な設定要素は下記の通りです。
要素 | 引数 | 設定例 |
---|---|---|
node の色 |
edge_color |
"色名" or "色名のリスト" など※デフォルト: 'C0' 薄い青 |
node のサイズ |
node_size |
"数値" or "数値のリスト" など※デフォルト: 300 |
edge の色 |
edge_color |
"色名" or "色名のリスト" など※デフォルト: 'black' |
edge の太さ |
width |
"数値" or "数値のリスト" など※デフォルト: 1 |
それぞれ、グラフ内で一括で設定する場合には、"色名"
や"数値"
を指定します。
要素ごとに設定する場合は、要素の数に応じた[リスト]
等で指定します。
それぞれ実行例を確認してみましょう。
一括で設定(色、ノードサイズ、エッジの太さ)
キーワード引数としてシリアル値(リストでなく1つの値)を与えると、グラフ全体に適用されます。
ノードの色を"red"
、エッジの色を"gray"
、太さを500
にしてみましょう。
nx.draw(G, with_labels=True,
node_color = "red",
edge_color = "gray",
node_size = 500,
width = 5)
plt.savefig("05_ネットワーク図の見た目変更(一括設定)例.png", bbox_inches="tight")
plt.show()
ネットワーク図の見た目が一括で変更されていますね。
要素毎に設定
[リスト]
で設定値を渡すことで、各ノード、エッジに異なる設定を適用できます。
各要素の順番に対応するように設定値を渡します。
次の例ではノードの各要素の色をcolors
というリストで渡しています。
colors = ["lightblue", "green", "red", "cyan", "magenta", "yellow"]
nx.draw(G, with_labels=True, node_color = colors)
plt.savefig("06_ネットワーク図の見た目変更(個別設定)例.png", bbox_inches="tight")
plt.show()
次のようにノードに色が設定されます。
色の設定で個別に色を設定する場合は、Matplotlib
のカラーサイクルを使うとキレイに色付けできます。
カラーマップを生成して、要素の数だけ色を準備しておきます。
- カラーマップ生成:
cmap = plt.get_cmap("Pastel1")
node
数の色を準備:node_color = [cmap(i) for i in range(G.number_of_nodes())]
サンプルコードで挙動を確認してみましょう。
# カラーマップから色の準備
cmap = plt.get_cmap("Pastel1")
node_color = [cmap(i) for i in range(G.number_of_nodes())]
# ネットワークの可視化
nx.draw_networkx(G, with_labels=True, node_color = node_color)
plt.savefig("01_simple_network.png", bbox_inches="tight")
plt.show()
次のようにカラーマップで色付けされたネットワーク図が作れます。
- 【参考】カラーマップの使い方については、次の記事で詳しく解説しています。
ネットワーク可視化例|次数に応じた配色・サイズ変更
最後に、各ノードの次数に応じて配色・サイズ変更する例を紹介します。
これまで、紹介してきた特性値の出力方法、ネットワーク図の見た目変更を組み合わせていきます。
実行例として、次のようなグラフを作成してみましょう。
このグラフは次の様なグラフになっています。
- ネットワーク図の
node
のサイズが次数の数に比例 - ネットワーク図と棒グラフで各
node
の色が対応
色やサイズの設定が適切に設定できれば簡単に作成できます。
簡単な工夫ですが、デフォルトのネットワーク図よりも視認性がだいぶ上がっていますね。
以下で実装例を紹介していきます。
次数に応じたネットワーク図の例
まずは、ノードの色付け、次数に応じたサイズ調整を行いましょう。
d = G.degree()
import numpy as np
np.array([d for n, d in G.degree()])
degrees = np.array([d for n, d in G.degree()])
degrees.mean()
# 各nodeの次数を取得
degrees = np.array([d for n, d in G.degree()])
# ノードの次数に比例するようにサイズを設定
sizes = 300 * degrees / degrees.mean()
print(sizes)
# [150.0, 450.0, 600.0, 150.0, 150.0, 300.0]
# カラーマップから色を生成
cmap = plt.get_cmap("Pastel1")
node_color = [cmap(i) for i in range(G.number_of_nodes())]
# グラフの出力
plt.figure(figsize = (10,5))
plt.subplot(121)
nx.draw(G, with_labels=True, node_color = node_color, node_size = sizes, edge_color = "black")
plt.show()
次のようにノードのサイズに応じたサイズ変更ができました。
情報をわかりやすくするために、次数に関する棒グラフを作成してみましょう。
次数順の棒グラフの作成
次に各ノードの次数順の棒グラフを作成してみましょう。
nodes_sorted_by_degree = sorted(G.degree(), key=lambda x: x[1], reverse=True)
print(nodes_sorted_by_degree)
# [('C', 4), ('B', 3), ('F', 2), ('A', 1), ('D', 1), ('E', 1)]
# 色のリストを次数順に並び替える
colors_dict = dict(zip(G.nodes(), node_color))
colors_sorted = [colors_dict[node] for node, _ in nodes_sorted_by_degree]
# グラフの出力
x, height = list(zip(*nodes_sorted_by_degree))
plt.subplot(122)
plt.xlabel("Number of degrees")
plt.ylabel("Name of node")
plt.barh(x, height, color = colors_sorted)
plt.savefig("08_次数の棒グラフ.png", bbox_inches="tight")
plt.show()
ネットワーク図の描画と合わせて実行すると、次の様なグラフができます。
- 【参考】
subplot
の使い方については、次の記事で詳しく解説しています。
- 【参考】
Matplotlib
での棒グラフ作成方法については、次の記事で詳しく解説しています。
最短経路の探索
NetworkX
にはネットワーク内の最短経路を探索する関数も準備されています。
特定の2点間の最短距離を探すには、nx.shortest_path()
関数を使用します。
nx.shortest_path(G, source=始点ノード, target=終点ノード)
サンプルコードで挙動を確認してみましょう。
nx.shortest_path(G, source="A", target="D")
# ['A', 'B', 'C', 'D']
最短経路の探索結果をネットワーク図内に表示する例を紹介します。
探索結果をもとにネットワーク図の見た目を変更すればOKです。
# カラーマップから色を生成
cmap = plt.get_cmap("Pastel1")
node_color = [cmap(i) for i in range(G.number_of_nodes())]
# 最短経路の探索
shortest_pass = nx.shortest_path(G, source="A", target="D")
highlited_edges = list(zip(shortest_pass, shortest_pass[1:]))
# 最短経路のエッジに色付け
G.add_edges_from(highlited_edges, color="tab:blue")
edge_color_dict = nx.get_edge_attributes(G, "color")
edge_color = [edge_color_dict[p] if p in edge_color_dict else "gray" for p in G.edges()]
# ネットワーク図の描画
nx.draw(G, node_color=node_color, edge_color=edge_color, with_labels=True)
plt.savefig("09_最短経路を強調.png", bbox_inches="tight")
plt.show()
次のように最短経路を探索して、強調表示したグラフを描けます。
NetworkX
の機能をうまく使えば、最短経路の描画も簡単ですね。
上の例ではMatplotlib
のデフォルトの色(tab:blue
)を使って、最短経路を強調表示しています。
他のグラフとの一貫性を保つために、Matplotlib
のデフォルトの色を使用することも多いです。
- 【参考】
Matplotlib
のデフォルトの色については、次の記事で詳しく解説しています。
ネットワーク分析おススメの1冊!
pythonでネットワーク分析をしたい方には、こちらの本がおススメです。
実は私はネットワーク分析の基本どころか、NetworkXの存在すらしらなかったのですが…。
この本では次の様な内容が詳しく解説されています。
- ネットワーク分析の流れや用語説明
NetworkX
をサンプルコード付きで解説
私のような初学者でもすぐに簡単なネットワーク分析をできるようになりました!
今回の記事では紹介しませんでしたが、本の中では次の様な内容についても解説しています。
- ネットワークの中心性を解析する方法
- グループの分割・抽出方法
ネットワーク分析の勉強にオススメな1冊でした!