はじめに
ネットワークグラフは、複雑なデータの関係性を視覚化するのに非常に役立ちます。しかし、3Dのネットワークグラフとなると、その作成方法がよく分からないという方も多いのではないでしょうか。
そこで本記事では、Pythonを使って3Dネットワークグラフを作成する方法を、初心者の方でも理解できるよう、丁寧に解説していきます。題材としては、ビクトル・ユーゴーの小説『レ・ミゼラブル』に登場するキャラクター同士の関係性を、3Dグラフで表現してみましょう。
3Dネットワークグラフを作成するメリット
3Dネットワークグラフを作成するメリットは主に以下の3点です。
- 複雑な関係性を視覚的に把握できる
- インタラクティブな操作で様々な角度から眺められる
- 見栄えのする洗練されたグラフになる
平面的な2Dグラフでは表現しきれない情報も、3D空間を活用することでより直感的に理解することができるのです。
必要なライブラリのインストール
3Dネットワークグラフを作成するために、以下のPythonライブラリを使用します。
- igraph:高性能なグラフ生成・解析用ライブラリ
- plotly:インタラクティブな可視化ライブラリ
まずはこれらのライブラリをインストールしましょう。igraphは以下のコマンドでインストールできます。
!pip install python-igraph
!pip install chart-studio
plotlyのインストール・設定方法については、公式ドキュメントを参照してください。
データの取得と準備
グラフ作成に必要なデータを、JSONファイルから読み込みます。ここでは、有名な小説『レ・ミゼラブル』の登場人物の関係性を表すデータを使用します。
import json
import urllib.request
import igraph as ig
data = []
req = urllib.request.Request("https://raw.githubusercontent.com/plotly/datasets/master/miserables.json")
opener = urllib.request.build_opener()
with opener.open(req) as f:
data = json.loads(f.read().decode('utf-8'))
print(data.keys())
JSONファイルからデータを読み込めたら、まずはグラフのノード数を確認しておきましょう。
N=len(data['nodes'])
print(N)
# 77
登場人物(ノード)の数は77のようですね。
続いて、登場人物同士の関係性(エッジ)をリスト化し、igraphのGraphオブジェクトを生成します。
L=len(data['links'])
Edges=[(data['links'][k]['source'], data['links'][k]['target']) for k in range(L)]
G=ig.Graph(Edges, directed=False)
最後に、各ノードの属性である「group」と「name」を取得しておきます。これらの情報は後でグラフ上に表示するのに使います。
labels=[]
group=[]
for node in data['nodes']:
labels.append(node['name'])
group.append(node['group'])
グラフレイアウトの設定
3Dグラフを作成するために、ノードの配置を決めるレイアウトアルゴリズムを選択する必要があります。ここでは、「Kamada-Kawai」と呼ばれる力学モデルを使用します。
layt=G.layout('kk', dim=3)
このレイアウトアルゴリズムでは、ノード間の引力と斥力のバランスを取ることで、グラフ全体の見た目を美しく整えることができます。
laytには、各ノードのx, y, z座標が格納されています。例えば、6番目のノードの座標は以下のように取得できます。
print(layt[5])
# [4.195949332184983, 1.172321178571202, -2.5543268281789135]
Plotlyグラフのデータ設定
次に、Plotlyを使ってグラフを描画するために必要なデータを準備します。
ノードの座標データを設定します。
Xn=[layt[k][0] for k in range(N)]# ノードのx座標
Yn=[layt[k][1] for k in range(N)]# y座標
Zn=[layt[k][2] for k in range(N)]# z座標
エッジの座標データを設定します。エッジは、始点と終点の座標に加えて、不連続性を示すNoneを含む必要があります。
Xe=[]
Ye=[]
Ze=[]
for e in Edges:
Xe+=[layt[e[0]][0],layt[e[1]][0], None]# エッジの端点のx座標
Ye+=[layt[e[0]][1],layt[e[1]][1], None]
Ze+=[layt[e[0]][2],layt[e[1]][2], None]
Plotlyによるグラフ描画
いよいよPlotlyを使ってグラフを描画していきます。ノードとエッジのデータを元に、3Dのscatterオブジェクトを作成します。
import plotly.graph_objs as go
# エッジ用のScatter3dオブジェクトを作成
trace1=go.Scatter3d(x=Xe, # エッジの端点のx座標のリスト
y=Ye, # エッジの端点のy座標のリスト
z=Ze, # エッジの端点のz座標のリスト
mode='lines', # 描画モードを'lines'に設定し、線で結ぶ
line=dict(color='rgb(125,125,125)', width=1), # エッジの線の色と幅を指定
hoverinfo='none' # hoverした際に表示する情報を'none'に設定し、何も表示しない
)
# ノード用のScatter3dオブジェクトを作成
trace2=go.Scatter3d(x=Xn, # ノードのx座標のリスト
y=Yn, # ノードのy座標のリスト
z=Zn, # ノードのz座標のリスト
mode='markers', # 描画モードを'markers'に設定し、点で表示
name='actors', # トレース名を'actors'に設定
marker=dict(symbol='circle', # マーカーの形状を'circle'(円形)に設定
size=6, # マーカーのサイズを6に設定
color=group, # マーカーの色を各ノードの所属グループに基づいて設定
colorscale='Viridis', # カラースケールを'Viridis'に設定
line=dict(color='rgb(50,50,50)', width=0.5) # マーカーの枠線の色と幅を指定
),
text=labels, # 各ノードのテキストラベルをlabels(登場人物の名前のリスト)に設定
hoverinfo='text' # hoverした際に表示する情報を'text'に設定し、テキストラベルを表示
)
ノードのマーカーの色は、先ほど取得した「group」に基づいて自動で設定されます。また、hover時にノードの「name」が表示されるよう設定しています。
続いて、グラフのレイアウトを整えます。背景を透明にしたり、タイトルや注釈を付けたりして、グラフを見やすくします。
# 3D軸の設定を定義
axis=dict(showbackground=False, # 背景を非表示に
showline=False, # 軸の線を非表示に
zeroline=False, # ゼロ線を非表示に
showgrid=False, # グリッドを非表示に
showticklabels=False, # 軸のラベルを非表示に
title='' # 軸のタイトルを空に
)
# レイアウトの設定
layout = go.Layout(
title="Network of co-appearances of characters in Victor Hugo's novel<br> Les Miserables (3D visualization)", # グラフのタイトルを設定
width=1000, # グラフの幅を1000pxに
height=1000, # グラフの高さを1000pxに
showlegend=False, # 凡例を非表示に
scene=dict( # 3D空間の設定
xaxis=dict(axis), # x軸の設定をaxisで定義した内容に
yaxis=dict(axis), # y軸の設定をaxisで定義した内容に
zaxis=dict(axis), # z軸の設定をaxisで定義した内容に
),
margin=dict( # 余白の設定
t=100 # 上部の余白を100pxに
),
hovermode='closest', # ホバー時に最も近いデータを強調表示
annotations=[ # 注釈の設定
dict(
showarrow=False, # 注釈の矢印を非表示に
text="Data source: <a href='http://bost.ocks.org/mike/miserables/miserables.json'>[1] miserables.json</a>", # 注釈のテキストを設定(データソースのリンクを含む)
xref='paper', # 注釈の位置指定をペーパー座標系(0~1)に
yref='paper', # 注釈の位置指定をペーパー座標系(0~1)に
x=0, # 注釈のx座標を0(左端)に
y=0.1, # 注釈のy座標を0.1に
xanchor='left', # 注釈のx座標の基準位置を左に
yanchor='bottom', # 注釈のy座標の基準位置を下に
font=dict(
size=14 # 注釈のフォントサイズを14pxに
)
)
], )
data=[trace1, trace2] # トレースのリスト(エッジ用とノード用)を作成
fig=go.Figure(data=data, layout=layout) # トレースとレイアウトからFigureオブジェクトを作成
グラフの表示
最後に、作成したグラフをPlotlyで表示します。
from plotly.offline import iplot
iplot(fig, filename='Les-Miserables')
ここまでの手順で、小説『レ・ミゼラブル』に登場する人物同士の共起関係を表現した3Dネットワークグラフが完成しました。
まとめ
本記事では、Pythonを使って3Dネットワークグラフを作成する方法を丁寧に解説してきました。
3Dグラフの作成には、igraphとPlotlyという2つのライブラリを活用します。igraphを使ってグラフデータを処理し、Plotlyを使ってインタラクティブな3D可視化を行うことができました。
本記事の内容を応用すれば、様々な分野のネットワークデータを分かりやすく可視化できるはずです。ただし、実際のデータを用いる際は、事前にデータの整形と前処理が必要となる点には注意が必要です。
3Dネットワークグラフは見た目のインパクトも大きく、複雑な関係性を直感的に伝えることができる素晴らしいツールです。本記事を参考に、ぜひ実際のデータを用いて3Dグラフを作成し、新たな知見を見出していってください。
以上、Pythonで3Dネットワークグラフを作成する方法の解説でした。読者の方の理解が深まり、グラフ作成の参考になれば幸いです。
コメント