networkxで生成したネットワークをCytoscapeで可視化する

# モチベーション
ネットワークを可視化するためにnetworkxを使っていたのですが、ノード数が多くなるとnetworkxで自動計算されるレイアウトだと見やすさに限界が出ます（ノードが重なり合ってつぶれてしまう）。そこで、networkxで生成したグラフを流用してより柔軟な可視化が可能なCytoscapeを用いた可視化を試してみます。

# 手順
ゴールは相関係数をもとにした各要素間のネットワークの可視化です。相関係数の計算とnetworkxでの可視化までは以下を参照しています。
- [networkxで重み付きグラフを描画する方法](https://qiita.com/okumakito/items/020c1075fb99ef90a2de)

改めて手順を確認すると
1. 相関係数行列を作成する
    ```python
      # generate a sample weight matrix
      n1 = 10
      n2 = 20
      n = n1 + n2
      np.random.seed(12345)
      X  = np.random.randn(n, 20)
      v = 0.6 * X[0]
      X[:n1] += v
      X[n1:] -= v
      W = np.corrcoef(X)
    ```
1. networkxを用いてグラフを作成する
    ```python
    G = nx.Graph()
    # Wは相関係数行列
    for i, j in zip(*np.triu_indices_from(W, 1)):
      G.add_edge(i, j, weight=W[i,j])
    ```
1. networkxでCytoscapeが対応している形式のjsonを生成する
    - **吐き出されるjsonをCytoscapeで読んでも解釈されなかった**
1. （上記の変換がうまくできなかったため）jsonのデータをもとにcsvに変換する
    ```python
    import pandas as pd

    # 空のDataFrameを用意する
    out = pd.DataFrame()
    for e in nx.cytoscape_data(G)["elements"]["edges"]:
        # "edges"の中に含まれるsource, target, weightの情報をDataFrameに追加する
        out = pd.concat(
            [
                out,
                pd.DataFrame(
                    {
                        "from": [e["data"]["source"]],
                        "to": [e["data"]["target"]],
                        "weight": [e["data"]["weight"]],
                    }
                )
            ]
        )
    out.reset_index(inplace=True, drop=True) # indexの振り直し
    out.to_csv("network.csv")
    ```
1. Cytoscapeでcsvを読み込む
    - networkのペインにcsvをドラッグすればok
    - source/destinationのカラムを指定してネットワーク図を生成する
    - 緑の○がsource/赤の○はdestination（今回はどっちでもいいはず）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116426/14d44f16-21de-3bbd-8d65-7a3f4cfca5fd.png)
    - 初期配置だとこう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116426/b16fd8b6-5de9-3fc7-b831-752425894169.png)
    - レイアウトを選ぶと整列する（circular layout）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116426/75266b9e-cea1-e3dc-884d-c22ffcc65871.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116426/32d7a9c2-cb07-5ccb-24a5-e49960e6fc6f.png)


1. 重みによってエッジの色を変更する
    - CytoscapeのStyleペインで`stroke color`を選択
    - weightに対応して色を付けたいので、columnにweightを選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116426/2abe6c45-7a92-0c1a-3727-a654e94fe007.png)

1, 2の手順は参照先で説明されているのでコード以外は割愛します

# コード全体
```python
import numpy as np
import pandas as pd
import networkx as nx

n1 = 10
n2 = 20
n = n1 + n2
np.random.seed(12345)
X = np.random.randn(n, 20)
v = 0.6 * X[0]
X[:n1] += v
X[n1:] -= v
W = np.corrcoef(X)

G = nx.Graph()

for i, j in zip(*np.triu_indices_from(W, 1)):
    G.add_edge(i, j, weight=W[i, j])
# ここまで流用

# ここからCytoscape用出力
out = pd.DataFrame()
for e in nx.cytoscape_data(G)["elements"]["edges"]:
    out = pd.concat(
         [
            out,
            pd.DataFrame(
                {
                    "from": [e["data"]["source"]],
                    "to": [e["data"]["target"]],
                    "weight": [e["data"]["weight"]],
                }
            )
        ]
     )
out.reset_index(inplace=True, drop=True)
out.to_csv("network.csv")
```

# tips
エッジの足切り
```python
for i, j in zip(*np.triu_indices_from(W, 1)):
    # 相関係数の閾値をもとに足切りできる
    if abs(weight[i, j]) > 0.8:
        G.add_edge(i, j, weight=W[i, j])
```

# おわりに
ノードはnetworkxを用いた場合に比べて個々が目視で分かるようになりました。
一方でエッジが多すぎて埋もれるものも出てしまいました。
理想はweightベースにエッジの長さがコントロールできれば良いのですが、一旦はノードのつぶれを改善できたので良いことにします。