Pythonでしか描けない美しい共起ネットワークを描こう！

## はじめに
![テキストアナリティクス](https://www.hanmoto.com/bd/img/9784065274101.jpg?2022-03-11T16%3A10%3A21%2B09%3A00)
この書籍に [**networkx**](https://pypi.org/project/networkx/)＋[**matplotlib**](https://pypi.org/project/matplotlib/) による **静的共起ネットワーク** が紹介されています。
[**以前の記事**](https://qiita.com/hima2b4/items/5619e617c34f588b418a) の通り、無事に描くことはできたのですが、
>- **ネットワークレイアウト（描画アルゴリズム）を選択できるようにしたい**
>- **ノードを部分ネットワークごとに色分けしたい**（※部分ネットワーク＝コミュニティ）
>- **コミュニティ分割のアルゴリズムを変更できるようにしたい**
>- **ノードの配置を決めるlayoutのパラメータkを変更できるようにしたい**
>- **ノードのサイズを変えたい**
>- **ノードテキスト（表記単語）の表記サイズをノードのサイズに応じ、大きくしたい**
>- **単語表記のフォントサイズを変えたい**

など、カスタマイズしてみたい！ ということでやってみましたという記事です。

なお、以下のサイトが 超参考になりました。

https://www.ogis-ri.co.jp/otc/hiroba/technical/similar-document-search/part6.html


## 実行したこと
>- Google Colabで実行する
>- データは「再エネ」でtweet検索したをつぶやきを [**ついすぽ**](https://chrome.google.com/webstore/detail/%E3%81%A4%E3%81%84%E3%81%99%E3%81%BD-tweet-export/imjojcgefiinokaclmifakpphkfeiddg?hl=ja) でcsvにエクスポートしたデータ。
>- 共起ネットワーク生成
>- 共起ネットワークの画像（Jpeg）を [**Plotly**](https://pypi.org/project/plotly/) で表示（強引！）


https://chrome.google.com/webstore/detail/ついすぽ-tweet-export/imjojcgefiinokaclmifakpphkfeiddg?hl=ja

## 共起ネットワーク
以下は、カスタマイズ前の共起ネットワークです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/e157038d-fea9-c32f-bc4e-7e6c82721ee8.png)

以下がカスタマイズ後です。
まずは、レイアウト（描画アルゴリズム）による違いからです。

### レイアウト（描画アルゴリズム）変更
**springレイアウト**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/ca1d491c-67c0-1215-8b59-9ef6fef7f31e.png)
layoutでspring, fruchterman_reingold を選択した場合のみ、パラメータk の値を変えるとノードの配置を変えることができます。
値を小さくすると コミュニティ毎に分かれてくれ、値を大きくするとコミュニティ同士が重なり合う感じです。パラメータk の設定範囲は0-1です。
以下は、springレイアウトにおいて、kの設定を小さく（0.04）して描画させたものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/b47e8c31-b8d5-f9ce-d7fc-e5d90dfeb483.png)

**kamada_kawaiレイアウト**
これは、[**nlplot**](https://pypi.org/project/nlplot/) に近いですね。これではテキストサイズがノードサイズに応じすこしだけ大きくなるように調整しています。（※あとで述べるフォームで比率が変えられるようにしています。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/a1fcc78e-2503-62c5-bc1f-7133ecb939ae.png)
**circularレイアウト**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/6de85b16-c925-aa0e-3da9-7b2fd6061da8.png)
**spiralレイアウト**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/72657156-8fa1-5e98-55d5-325185d23ce4.png)
**fruchterman_reingoldレイアウト**
springレイアウトと近いですね。どうもアルゴリズムは同じようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/e847e77c-b39d-1a9d-b55b-f446273eb9c7.png)
**randomレイアウト**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/5198d279-14f1-92a0-177c-a8f3445c5ac8.png)
**spectralレイアウト**
これは・・・おまけですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/d7934377-f267-ad1b-61e4-51bf9acd0545.png)


### コミュニティ検出アルゴリズム変更
[**networkx**](https://pypi.org/project/networkx/) は いくつかの **コミュニティ検出アルゴリズム** が実行できます。
コミュニティ検出アルゴリズムは、さまざまなネットワークで密に接続されたコンポーネントグループを明確にしてくれます。

community_algorithmによる違いは以下の通りです。（※レイアウトは**springレイアウト**としています。）

**greedy_modularity**
![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/93b7b20c-b0cc-ace8-d564-c1057c73f773.png)
**label_propagation**
![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/551ee5c3-3e97-52a5-e54e-913066c35a8a.png)
**connected_components**
![ダウンロード (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/22e2e2c2-4517-84cf-3bf7-0de8c6168b47.png)

***
各種調整し、以下に落ち着きました。
![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/200f7f0e-cbd9-b014-e656-da617c1e1241.png)
上記の描画条件は以下です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/0b26a787-3726-00ed-8f2e-9af8f8a7c7a5.png)


## コードに関する補足
**[テキストアナリティクス入門](www.amazon.co.jp/dp/4065274109)** に掲載されているコードからの差分についてのみ、以下に補足します。

まず、共起ネットワークのカスタマイズ変数をGoogle colabのフォーム機能を利用しセット。

```Python:共起ネットワーク見た目調整の変数をフォームにセット
#@title 共起ネットワーク表示

#@markdown  **<font color= "Crimson">ガイド</font>：layout** でネットワークレイアウトが選択できます。**community_algorithm** でコミュニティ分割のアルゴリズムが選択できます。（greedy_modularityはモジュラリティ最適化、label_propagationはラベル伝搬、connected_componentsは連結成分）｜**weight_cuttoff** で一定の割合に満たない共起を除外（枝切り）できます。｜**node_size** で円の大きさ、**text_size** で単語表記サイズが変更でき、**node_fit_rate**でノードサイズに応じたサイズアップレートが指定できます。｜**layout_parameter_k** はノード間の反発力の設定です。**k**が大きいほどノードの配置は円形に近くなります。（この設定はspringとfruchtermanレイアウト時のみ有効）

layout = 'kamada_kawai' #@param ['spring','fruchterman_reingold','kamada_kawai','spectral','spiral','circular','random']
community_algorithm = 'greedy_modularity' #@param ['greedy_modularity','label_propagation','connected_components']
weight_cutoff = 0.025 #@param {type:"slider", min:0.01, max:0.05, step:0.005}
node_size_ = 3500 #@param {type:"slider", min:1000, max:6000, step:500}
text_size = 9 #@param {type:"slider", min:5, max:30, step:1}
text_size_node_fit_rate = 0.3 #@param {type:"slider", min:0, max:1, step:0.05}
layout_parameter_k  = 0.07 #@param {type:"slider", min:0.01, max:1, step:0.01}
```
上記コードにより、Google Colabのセルに以下のフォームが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/5a63c67c-9037-c05a-dd28-6764db32df93.png)


**コミュニティライブラリをインポート**
```Python:コミュニティ検出のライブラリ
from networkx.algorithms.community import greedy_modularity_communities
from networkx.algorithms.community import label_propagation_communities
```

```Python:レイアウトとコミュニティアルゴリズムの選択と実行
def pyplot_network (G):
    plt.figure(figsize=(20, 11), dpi=300)

    if layout == 'spring':
      pos = nx.spring_layout(G, k=layout_parameter_k,iterations=50, threshold=0.0001, weight='weight', scale=1, center=None, dim=2, seed=123)

    elif layout == 'fruchterman_reingold':
      pos = nx.fruchterman_reingold_layout(G, dim=2, k=layout_parameter_k, pos=None, fixed=None, iterations=50, weight='weight', scale=1.0, center=None)

    elif layout == 'spectral':
      pos = nx.spectral_layout(G, weight='weight', scale=1, center=None, dim=2)

    elif layout == 'spiral':
      pos = nx.spiral_layout(G, scale=1, center=None, dim=2, resolution=0.35, equidistant=False)

    elif layout == 'kamada_kawai':
      pos = nx.kamada_kawai_layout(G, dist=None, pos=None, weight=1, scale=1, center=None, dim=2)

    elif layout == 'circular':
      pos = nx.circular_layout(G, scale=1, center=None, dim=2)

    else:
      pos = nx.random_layout(G, center=None, dim=2, seed=None)

    connecteds = []
    colors = []

    if community_algorithm == 'greedy_modularity':
      for i, c in enumerate(greedy_modularity_communities(G)):
        connecteds.append(c)
        colors.append(1/20 * i)

    if community_algorithm == 'label_propagation':
      for i, c in enumerate(label_propagation_communities(G)):
        connecteds.append(c)
        colors.append(1/20 * i)

    else:
      for i, c in enumerate(nx.connected_components(G)):
        connecteds.append(c)
        colors.append(1/20 * i)

    node_colors = []
    for node in G.nodes():
      for i, c in enumerate(connecteds):
        if node in c:
          node_colors.append(colors[i])
          break
```
テキスト表示は、**nx.draw_networkx_labels()** ではなく、ノードサイズに応じたテキストサイズにするため、以下のコードとしています。
**text_size**でテキスト表示の最小フォントサイズを指定し、**text_size_node_fit_rate** でノードサイズに応じてフォントを大きくして表示させるというものです。
```Python:テキストをノードサイズに応じて大きく表示
    from matplotlib.pyplot import figure, text
    d = dict(G.degree)
    for node, (x, y) in pos.items():
      text(x, y, node, fontsize=text_size+d[node]*text_size_node_fit_rate, ha='center', va='center')

    plt.axis ("off")
    plt.savefig("networkx.jpeg")
    plt.show()
```

また、以下の引数を追加しています。
>- nx.draw_networkx_nodes()の引数に node_color=node_colors, alpha=0.3 を追加
>- nx.draw_networkx_edges()の引数に alpha=0.4, edge_color="darkgrey" を追加


※alphaは、透明度です。

## ムリヤリPlotly表示
Jpeg保存した共起ネットワーク（ファイル名「networkx.jpeg」）をムリヤリ [**Plotly**](https://pypi.org/project/plotly/) で表示させました。
解像度をある程度確保しないときびしいので、dpi=300 としました。

あとはムリヤリ描くだけ・・・
```Python:共起ネットワーク画像をPlotly表示
#@title 共起ネットワーク表示（Plotly）
import plotly.express as px
from skimage import io
img = io.imread('networkx.jpeg')

fig = px.imshow(img)
fig.update_layout(width=1500, height=1000, margin=dict(l=10, r=10, b=10, t=10))
fig.update_xaxes(showticklabels=False).update_yaxes(showticklabels=False)
fig.show()
```
こんな風に拡大できる・・・ただそれだけ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/d2828ce1-28d0-1cf8-a127-e5f36e06e7ea.png)


## 最後に
[**nlplot**](https://pypi.org/project/nlplot/) には敵わないですが、自分好みに見た目をカスタマイズしたいな・・・というときはよいとおもいます。
肝心なのはテキストを分析することなので、美しく描いてモチベをあげることと、共有しやすくすることを通して、よりよき前進を図りたいというところです。

それにしても
 [**networkx**](https://pypi.org/project/networkx/) はすごいです。いろんなことができすぎ。。。ですね。

***

 [**networkx**](https://pypi.org/project/networkx/) で共起ネットワークを描きたい！という方は、ぜひ[**「テキストアナリティクス入門」**](https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5Data-Science%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-Python%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%A2%E3%83%8A%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AF%E3%82%B9%E5%85%A5%E9%96%80-KS%E6%83%85%E5%A0%B1%E7%A7%91%E5%AD%A6%E5%B0%82%E9%96%80%E6%9B%B8-%E5%89%9B%E5%8F%B2/dp/4065274109/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=)を手にされることをおすすめします。（私は回し者ではありません）

GoogleColabで実行するために必要なことについては、以下で触れています。

https://qiita.com/hima2b4/items/5619e617c34f588b418a


## 参考
https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5Data-Science%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-Python%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%A2%E3%83%8A%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AF%E3%82%B9%E5%85%A5%E9%96%80-KS%E6%83%85%E5%A0%B1%E7%A7%91%E5%AD%A6%E5%B0%82%E9%96%80%E6%9B%B8-%E5%89%9B%E5%8F%B2/dp/4065274109/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=

https://www.ogis-ri.co.jp/otc/hiroba/technical/similar-document-search/part6.html

https://networkx.guide/algorithms/community-detection/

https://stackoverflow.com/questions/62649745/is-it-possible-to-change-font-sizes-according-to-node-sizes

https://qiita.com/hima2b4/items/5619e617c34f588b418a