Pythonプログラミング：Qiita記事のタグからNetworkX+Plotlyを使ってインタラクティブなグラフを描画してみた

# はじめに

先日、公開されましたね。ガートナーさんの先進テクノロジのハイプ・サイクル（2022年版）[^1][^2]。
英日それぞれで関連記事が出始めているので、私もちょこちょこWeb記事を拝読している今日この頃。。。

さて、Web記事を眺めていると、コーザルAI（Causal AI）もキーワードとしては登場しており、そこから連想して過去の投稿を思い出しました。

https://qiita.com/Blaster36/items/fcfe892fd90cd0c0bed3

当時は可視化することが目的で、満足していました。
が、最近のQiita記事の動向を把握するための一環として、違った可視化の方法を試してみようと思い至りました。

というわけで、NetworkX[^3]を使った可視化で面白い有志の投稿を見つけたので、試してみました。
結果的には、大満足。ネットワークグラフが、、、Plotly[^4]でいとも簡単にきれいな仕上がりになるものかと感動しました。

## 本稿で紹介すること

- Plotlyを使ったネットワークグラフの描画

## 本稿で紹介しないこと

- ガートナーさん公開のハイプ・サイクルの内容

# Plotlyを使ったネットワークグラフの描画

以下、2つの投稿を参考にさせていただきました。

https://irukanobox.blogspot.com/2021/02/python.html

https://gist.github.com/kohiro37/d8dd770421e89fbf2c2907d4400931f0

掲載されたCodeのうち、**build_interactive_network**関数がPlotlyを使って描画する部分と理解しました。
こちらを、試しました。

## 環境の準備

参考、GitHubのCodeに記載の動作環境を目標に、筆者の環境は以下の仕上がりになりました。
VirtualBox上のGuestOSとして、Ubuntuを準備しました。（いつものように、Vagrantから起動）

【動作環境】
- Ubuntu 20.04.4 LTS
- Python 3.8.10
- Node.js 18.7.0
- MeCab 0.996
- Graphviz 2.43.0

:::note info
筆者の環境は、参考に比べてPythonとNode.jsのVer.が上がっています。
:::

以下、Jupyter関係のコンポーネント群のVer情報です。

```shell:
$ jupyter --version
Selected Jupyter core packages...
IPython          : 8.4.0
ipykernel        : 6.15.1
ipywidgets       : 8.0.1
jupyter_client   : 7.3.4
jupyter_core     : 4.11.1
jupyter_server   : 1.18.1
jupyterlab       : 3.4.5
nbclient         : 0.6.6
nbconvert        : 6.5.3
nbformat         : 5.4.0
notebook         : 6.4.12
qtconsole        : 5.3.1
traitlets        : 5.3.0
```

あと、Qiita記事のタグを描画するのですが、当然日本語を含むので、フォントもインストールしました。

```shell:フォントのインストール
$ sudo apt install -y fonts-ipafont
$ sudo apt install -y fonts-ipaexfont
$ fc-cache -fv
$ fc-list | grep -i ipa
```

環境Setupのコマンド群も、末尾記載のGitHubで公開しました。

# まずは、現状把握

:::note warn
筆者は、自宅PCからプロバイダ網抜けてインターネット接続ありの環境で実行しています。
企業（Proxy環境）の方はお気を付けくださいませ。
:::

過去の投稿を懐かしみながら、同じコードを一旦流し切りました。
すると、こんな結果が得られました。
オブジェクトが重なっており、詳細を確認できない状態。。。ちょっと惜しい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/ac3fc6e2-c9a7-6cf7-a68b-941a6eee0058.png)

# いざ、Plotlyで描画

参考にしたのは、以下のPlotlyを使ってインタラクティブなグラフを描画するCode部分。

```python
def build_interactive_network(G, pos, node_sizes, node_colors):
     
    # edgeデータの作成
    edge_x = []
    edge_y = []
    for edge in G.edges():
        x0, y0 = pos[edge[0]]
        x1, y1 = pos[edge[1]]
        edge_x.append(x0)
        edge_x.append(x1)
        edge_x.append(None)
        edge_y.append(y0)
        edge_y.append(y1)
        edge_y.append(None)
 
    edge_trace = go.Scatter(
        x=edge_x, y=edge_y,
        line=dict(width=0.5, color='#888'),
        hoverinfo='none',
        mode='lines')
 
    # nodeデータの作成
    node_x = []
    node_y = []
    for node in G.nodes():
        x, y = pos[node]
        node_x.append(x)
        node_y.append(y)
 
    # nodeの色、サイズ、マウスオーバーしたときに表示するテキストの設定
    node_trace = go.Scatter(
        x=node_x,
        y=node_y,
        text=list(G.nodes()),
        hovertext=node_sizes,
        textposition='top center',
        mode='markers+text',
        hoverinfo='text',
        marker=dict(
            showscale=True,
            colorscale='Portland',
            reversescale=False,
            color=node_colors,
            size=node_sizes,
            colorbar=dict(
                thickness=15,
                title='Page Ranking',
            ),
            line_width=2))
    
    data = [edge_trace, node_trace]

    # レイアウトの設定
    layout=go.Layout(
                paper_bgcolor='rgba(0,0,0,0)',
                plot_bgcolor='rgba(0,0,0,0)',
                showlegend=False,
                hovermode='closest',
                margin=dict(b=10, l=5, r=5, t=10),
                font=dict(size=10),
                xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
                yaxis = dict(showgrid = False, zeroline = False, showticklabels = False))

    fig = go.Figure(data=data, layout=layout)
    fig.show()
```

上記関数を呼び出すCode部分がこちら。同じく参考のGitHubだと、LL.206～218に相当。
第三引数、Nodeの大きさのところを調整した程度でした。

```python
# nodeの配置方法の指定
seed = 0
np.random.seed(seed)
pos = nx_agraph.graphviz_layout(
    G,
    prog='neato',
    args='-Goverlap="scalexy" -Gsep="+6" -Gnodesep=0.8 -Gsplines="polyline" -GpackMode="graph" -Gstart={}'.format(seed))

# nodeの色をページランクアルゴリズムによる重要度により変える
pr = nx.pagerank(G)

# インタラクティブな共起ネットワークの可視化
build_interactive_network(G, pos, list([1000*v for v in pr.values()]), list(pr.values()))
```

実行すると、以下のようになりました。
- NodeのPageRank値が大きいと、配色が赤系になる。
- Node（を表す単語）の登場回数が多いと、サイズが大きくなる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/c0029ee9-e2d6-5e5c-2a42-fcd3715aab0e.png)

Plotlyの描画で何が魅力的かというと、拡大縮小が自由自在なのです。
マウスで範囲選択をすると、その範囲が拡大されより細かくEdgeのつながりを見ることができました。
例えば、JavaScriptやPythonを含むように範囲選択をした結果が以下。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/76958bcc-b650-27fa-2262-f1daec33a8be.png)

もっとビジーなグラフになっても、Plotlyなら無問題。範囲選択して拡大するだけ（ダブルクリックで全体俯瞰の初期状態へ戻る）。

ちなみに、描画したグラフをHTML形式で保管するには、以下のCodeを実行するだけです。
WebブラウザでHTMLファイルを開けば、Jupyter上で表示したグラフを見ることができます。

```python:
fig.write_html("qiita.html") # オフライン利用のためにHTML出力
```

# Notebook（Pythonコード）

GitHubで公開しました。

https://github.com/Blaster36/Script/blob/main/Qiita/coonw_qiita.ipynb

# まとめ

NetworkX＋Plotlyでインタラクティブかつ美しいグラフ描画を試しました。
PlotlyならHTML形式で出力もできるので、ちょっとWebブラウザでDEMOしたいときなんかも非常に重宝するかと思いました。


[^1]: Gartner Hype Cycle for Cloud Platform Services 2022 Positions Two Technologies to Reach the Plateau of Productivity in Less Than Two Years | https://www.gartner.com/en/newsroom/press-releases/2022-08-04-cloud-platform-hc-press-release
[^2]: Gartner、「先進テクノロジのハイプ・サイクル：2022年」を発表 | https://www.gartner.co.jp/ja/newsroom/press-releases/pr-20220816
[^3]: NetworkX | https://networkx.org/
[^4]: Plotly Python Graphing Library | https://plotly.com/python/