python(networkx)によるネットワーク分析メモ1 ネットワーク分析の基礎

pythonによるネットワーク分析のメモ書きとなります。
networkxを使用します。

内容・コードがまとまっていないので、詳細については参考書の参照をお願いします。

機会があればしっかり勉強していこうと思います。

### ネットワークの基礎知識
ネットワークは、頂点(ノード)とそれをつなぐ辺(エッジ、リンク)によって構成される。
ネットワークすべての辺を並べて表すデータ構造を辺リストと呼ぶ。


```python
import pandas as pd
import numpy as np

d1 = pd.read_csv('pos.csv', encoding='shift-jis')
```


```python
import networkx as nx
import matplotlib.pyplot as plt
import japanize_matplotlib

styles = {'node_size':1400,
          'font_size':15,
          'node_color':'lightblue',
          'with_labels':True,
          'font_weight':'bold',
          'font_family':'MS Gothic'}

# Graphオブジェクトの作成
G1 = nx.Graph()

# nodeデータの追加
nodes1 = np.unique([d.形態素1]+[d.形態素2])
edges1 = [(d.loc[i, '形態素1'], d.loc[i, '形態素2']) for i in range(len(d))]

G1.add_nodes_from(nodes1)
G1.add_edges_from(edges1)

# ネットワークの可視化
plt.figure(figsize=(6,6))
nx.draw(G1, **styles)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/4371c5ba-e5a5-d526-afb9-b77b54a7353f.png)
    

描画方法には様々なものがある。


```python
plt.figure(figsize=(25,8))
# ランダム
plt.subplot(131)
nx.draw_random(G1, **styles)

# 円周上
plt.subplot(132)
nx.draw_circular(G1, **styles)

# ばね配置
plt.subplot(133)
nx.draw_spring(G1, **styles)
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/98fd72e0-37f9-e971-58d7-a2a3693bbef7.png)
    


頂点の数や頂点のリスト、辺の数や辺のリストは次のように表示される。


```python
print('number of nodes:', G1.number_of_nodes())
print(G1.nodes())
print('number of edges:', G1.number_of_edges())
print(G1.edges())
```

    number of nodes: 9
    ['サ変名詞', '副詞', '副詞的名詞', '動詞', '動詞性接尾辞', '句点', '接続助詞', '普通名詞', '格助詞']
    number of edges: 14
    [('サ変名詞', '動詞'), ('サ変名詞', '普通名詞'), ('サ変名詞', '接続助詞'), ('サ変名詞', '格助詞'), ('副詞', '動詞'), ('副詞', '格助詞'), ('副詞的名詞', '格助詞'), ('副詞的名詞', '接続助詞'), ('動詞', '動詞性接尾辞'), ('動詞', '句点'), ('動詞性接尾辞', '動詞性接尾辞'), ('動詞性接尾辞', '普通名詞'), ('接続助詞', '普通名詞'), ('普通名詞', '格助詞')]
    

ネットワークを表すデータ構造として、隣接行列がよく用いられる。


```python
print('sparse adjacency matrix:')
print(nx.adjacency_matrix(G1))
print('dense adjacency matrix:')
print(nx.adjacency_matrix(G1).todense().astype(int))
```

    sparse adjacency matrix:
      (0, 3)	1.0
      (0, 6)	1.0
      (0, 7)	1.0
      (0, 8)	1.0
      (1, 3)	1.0
      (1, 8)	1.0
      (2, 6)	1.0
      (2, 8)	1.0
      (3, 0)	1.0
      (3, 1)	1.0
      (3, 4)	1.0
      (3, 5)	1.0
      (4, 3)	1.0
      (4, 4)	1.0
      (4, 7)	1.0
      (5, 3)	1.0
      (6, 0)	1.0
      (6, 2)	1.0
      (6, 7)	1.0
      (7, 0)	1.0
      (7, 4)	1.0
      (7, 6)	1.0
      (7, 8)	1.0
      (8, 0)	1.0
      (8, 1)	1.0
      (8, 2)	1.0
      (8, 7)	1.0
    dense adjacency matrix:
    [[0 0 0 1 0 0 1 1 1]
     [0 0 0 1 0 0 0 0 1]
     [0 0 0 0 0 0 1 0 1]
     [1 1 0 0 1 1 0 0 0]
     [0 0 0 1 1 0 0 1 0]
     [0 0 0 1 0 0 0 0 0]
     [1 0 1 0 0 0 0 1 0]
     [1 0 0 0 1 0 1 0 1]
     [1 1 1 0 0 0 0 1 0]]
    

### 次数
ネットワークの頂点の次数とは、その頂点につながる辺の数を表す。
次数ごとにその時数を持つ頂点数を表したものを次数分布と呼ぶ。


```python
print('degree:',G1.degree())
print(nx.degree_histogram(G1))
print(nx.info(G1))
plt.bar(range(5), height=nx.degree_histogram(G1))
```

    degree: [('サ変名詞', 4), ('副詞', 2), ('副詞的名詞', 2), ('動詞', 4), ('動詞性接尾辞', 4), ('句点', 1), ('接続助詞', 3), ('普通名詞', 4), ('格助詞', 4)]
    [0, 1, 2, 1, 5]
    Name: 
    Type: Graph
    Number of nodes: 9
    Number of edges: 14
    Average degree:   3.1111
    




    <BarContainer object of 5 artists>


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/b150cca0-d4c3-8bf6-bb9a-69da2c384c7e.png)

    
#### 有向グラフ・無向グラフ
ネットワークの辺に向きがあるものを有向グラフ、ないものを無向グラフと呼ぶ。
有向グラフでは、辺に向きがあるため頂点に入ってくる入次数と、頂点から出ていく出次数の2つがある。


```python
# Graphオブジェクトの作成
G1 = nx.Graph()

# nodeデータの追加
v = np.unique([d1.形態素1]+[d1.形態素2])
G1.add_nodes_from(v)
 
# edgeデータの追加
G1.add_edges_from([(d1.loc[i, '形態素1'], d1.loc[i, '形態素2']) for i in range(len(d1))])

# ネットワークの可視化
plt.figure(figsize=(6,6))
nx.draw_circular(G1, **styles)

print(nx.info(G1))
```

    Name: 
    Type: Graph
    Number of nodes: 9
    Number of edges: 14
    Average degree:   3.1111
    


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/b19864d2-f3ce-1826-eca1-0b47366ca550.png)




```python
# Graphオブジェクトの作成
DG1 = nx.DiGraph(G1)

plt.figure(figsize=(6,6))
nx.draw_circular(DG1, **styles)

print(nx.info(DG1))
```

    Name: 
    Type: DiGraph
    Number of nodes: 9
    Number of edges: 27
    Average in degree:   3.0000
    Average out degree:   3.0000
    


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/850f87c5-83fd-7a69-4511-5b7d01a0c4ae.png)


#### 多重辺、自己ループ
2点間をつなぐ複数の辺を多重辺、同一頂点を結ぶ辺を自己ループと呼ぶ。


```python
d2 = pd.concat([d1, pd.DataFrame({'形態素1':['普通名詞','普通名詞','普通名詞'],
                                  '形態素2':['普通名詞','動詞','動詞'],
                                  '共起':[1,2,1]})]).reset_index(drop=False)
d2
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>index</th>
      <th>形態素1</th>
      <th>形態素2</th>
      <th>共起</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0</td>
      <td>普通名詞</td>
      <td>接続助詞</td>
      <td>2</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1</td>
      <td>普通名詞</td>
      <td>格助詞</td>
      <td>2</td>
    </tr>
    <tr>
      <th>2</th>
      <td>2</td>
      <td>サ変名詞</td>
      <td>動詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>3</th>
      <td>3</td>
      <td>サ変名詞</td>
      <td>普通名詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>4</th>
      <td>4</td>
      <td>副詞</td>
      <td>動詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>5</th>
      <td>5</td>
      <td>副詞的名詞</td>
      <td>格助詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>6</th>
      <td>6</td>
      <td>動詞</td>
      <td>動詞性接尾辞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>7</th>
      <td>7</td>
      <td>動詞</td>
      <td>句点</td>
      <td>1</td>
    </tr>
    <tr>
      <th>8</th>
      <td>8</td>
      <td>動詞性接尾辞</td>
      <td>動詞性接尾辞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>9</th>
      <td>9</td>
      <td>動詞性接尾辞</td>
      <td>普通名詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>10</th>
      <td>10</td>
      <td>接続助詞</td>
      <td>サ変名詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>11</th>
      <td>11</td>
      <td>接続助詞</td>
      <td>副詞的名詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>12</th>
      <td>12</td>
      <td>格助詞</td>
      <td>サ変名詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>13</th>
      <td>13</td>
      <td>格助詞</td>
      <td>副詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>14</th>
      <td>14</td>
      <td>格助詞</td>
      <td>普通名詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>15</th>
      <td>0</td>
      <td>普通名詞</td>
      <td>普通名詞</td>
      <td>1</td>
    </tr>
    <tr>
      <th>16</th>
      <td>1</td>
      <td>普通名詞</td>
      <td>動詞</td>
      <td>2</td>
    </tr>
    <tr>
      <th>17</th>
      <td>2</td>
      <td>普通名詞</td>
      <td>動詞</td>
      <td>1</td>
    </tr>
  </tbody>
</table>
</div>




```python
nodes2 = np.unique([d2.形態素1]+[d2.形態素2])
edges2 = [(d2.loc[i, '形態素1'], d2.loc[i, '形態素2']) for i in range(len(d2))]

MG1 = nx.MultiGraph()
MG1.add_nodes_from(nodes2)
MG1.add_edges_from(edges2)

# ネットワークの可視化
plt.figure(figsize=(6,6))
nx.draw_circular(MG1, **styles)

print('degree:', MG1.degree())
print(nx.info(MG1))
```

    degree: [('サ変名詞', 4), ('副詞', 2), ('副詞的名詞', 2), ('動詞', 6), ('動詞性接尾辞', 4), ('句点', 1), ('接続助詞', 3), ('普通名詞', 9), ('格助詞', 5)]
    Name: 
    Type: MultiGraph
    Number of nodes: 9
    Number of edges: 18
    Average degree:   4.0000
    


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/b206d0bd-db76-5d0a-f6f7-6367ddb2f783.png)
    

#### 2部グラフ
２種類の頂点から構成され、異なる種類の頂点にしか間にしか辺が存在しないグラフを2部グラフと呼ぶ。
「論文とその著者」など。
２部グラフから同一種類の頂点のグラフを生成する方法として、プロジェクションがある。
もとの2部グラフにおいて、距離2でつながっている頂点を辺で結んだグラフを生成するものである。


```python
pokemon1 = pd.DataFrame({'ポケモン':['ピカチュウ','ピカチュウ','ライチュウ','ビードル','ビードル','メノクラゲ','メノクラゲ','ヤドラン','ヤドラン','パウワウ'],
                        '図鑑単語':['森','電気','電気','森','毒','海','毒','海','毒','海']})
pokemon1
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>ポケモン</th>
      <th>図鑑単語</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>ピカチュウ</td>
      <td>森</td>
    </tr>
    <tr>
      <th>1</th>
      <td>ピカチュウ</td>
      <td>電気</td>
    </tr>
    <tr>
      <th>2</th>
      <td>ライチュウ</td>
      <td>電気</td>
    </tr>
    <tr>
      <th>3</th>
      <td>ビードル</td>
      <td>森</td>
    </tr>
    <tr>
      <th>4</th>
      <td>ビードル</td>
      <td>毒</td>
    </tr>
    <tr>
      <th>5</th>
      <td>メノクラゲ</td>
      <td>海</td>
    </tr>
    <tr>
      <th>6</th>
      <td>メノクラゲ</td>
      <td>毒</td>
    </tr>
    <tr>
      <th>7</th>
      <td>ヤドラン</td>
      <td>海</td>
    </tr>
    <tr>
      <th>8</th>
      <td>ヤドラン</td>
      <td>毒</td>
    </tr>
    <tr>
      <th>9</th>
      <td>パウワウ</td>
      <td>海</td>
    </tr>
  </tbody>
</table>
</div>




```python
from networkx.algorithms import bipartite

B1 = nx.Graph()
B1.add_nodes_from(list(np.unique(pokemon1['ポケモン'])), bipartite=0)
B1.add_nodes_from(list(np.unique(pokemon1['図鑑単語'])), bipartite=1)
B1.add_edges_from([(pokemon1.loc[i, 'ポケモン'], pokemon1.loc[i, '図鑑単語']) for i in range(len(pokemon))])

plt.figure(figsize=(30,8))
plt.subplot(131)
nx.draw(B1, **styles)

top_nodes = set(n for n, d in B1.nodes(data=True) if d['bipartite']==0)
bottom_nodes = set(B1) - top_nodes
print('top nodes :',top_nodes)
print('bottom nodes :',bottom_nodes)

GT = bipartite.projected_graph(B1, top_nodes)
print('projected graph (top nodes):', GT.edges())

plt.subplot(132)
nx.draw(GT, **styles)

GB = bipartite.projected_graph(B1, bottom_nodes)
print('projected graph (bottom nodes):', GB.edges())

plt.subplot(133)
nx.draw(GB, **styles)
```

    top nodes : {'ピカチュウ', 'ヤドラン', 'ビードル', 'ライチュウ', 'パウワウ', 'メノクラゲ'}
    bottom nodes : {'毒', '電気', '海', '森'}
    projected graph (top nodes): [('ピカチュウ', 'ビードル'), ('ピカチュウ', 'ライチュウ'), ('ヤドラン', 'パウワウ'), ('ヤドラン', 'ビードル'), ('ヤドラン', 'メノクラゲ'), ('ビードル', 'メノクラゲ'), ('パウワウ', 'メノクラゲ')]
    projected graph (bottom nodes): [('毒', '海'), ('毒', '森'), ('電気', '森')]
    

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/31e0aa4d-3573-f47c-1002-f575e799fcfb.png)
    


### パス
頂点Aと辺でつながっている他の頂点に移ることを繰り返して、最終的にBに到着したとき、この辺の集合を頂点と頂点Bの間のパスと呼ぶ。
また、パスの中の辺の数をパスの長さと呼ぶ。

2頂点間の長さ$r$のパスの有無は隣接行列の積によって求めることができる。
長さ2の場合は$A^2$、長さ3の場合は$A^3$となる。


```python
# ネットワークの可視化
plt.figure(figsize=(6,6))
nx.draw_circular(G1, node_size=1500, node_color='lightblue', with_labels=True, font_family='MS Gothic')
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/314f5eb6-eb27-511f-2e52-52243b075553.png)
    


```python
Z = nx.adjacency_matrix(G).todense().astype(int)
print('adjacency matrix A:')
print(Z)
print('A*A:')
print(Z**2)
print('degree:', G.degree())
print('A*A*A')
print(Z**3)
```

    adjacency matrix A:
    [[0 0 0 1 0 0 1 1 1]
     [0 0 0 1 0 0 0 0 1]
     [0 0 0 0 0 0 1 0 1]
     [1 1 0 0 1 1 0 0 0]
     [0 0 0 1 1 0 0 1 0]
     [0 0 0 1 0 0 0 0 0]
     [1 0 1 0 0 0 0 1 0]
     [1 0 0 0 1 0 1 0 1]
     [1 1 1 0 0 0 0 1 0]]
    A*A:
    [[4 2 2 0 2 1 1 2 1]
     [2 2 1 0 1 1 0 1 0]
     [2 1 2 0 0 0 0 2 0]
     [0 0 0 4 1 0 1 2 2]
     [2 1 0 1 3 1 1 1 1]
     [1 1 0 0 1 1 0 0 0]
     [1 0 0 1 1 0 3 1 3]
     [2 1 2 2 1 0 1 4 1]
     [1 0 0 2 1 0 3 1 4]]
    degree: [('サ変名詞', 4), ('副詞', 2), ('副詞的名詞', 2), ('動詞', 4), ('動詞性接尾辞', 4), ('句点', 1), ('接続助詞', 3), ('普通名詞', 4), ('格助詞', 4)]
    A*A*A
    [[ 4  1  2  9  4  0  8  8 10]
     [ 1  0  0  6  2  0  4  3  6]
     [ 2  0  0  3  2  0  6  2  7]
     [ 9  6  3  1  7  4  2  4  2]
     [ 4  2  2  7  5  1  3  7  4]
     [ 0  0  0  4  1  0  1  2  2]
     [ 8  4  6  2  3  1  2  8  2]
     [ 8  3  2  4  7  2  8  5  9]
     [10  6  7  2  4  2  2  9  2]]
    

#### サイクル
サイクルとは、始点と終点が同一頂点となるパスのことである。


```python
# ネットワークの可視化
plt.figure(figsize=(6,6))
nx.draw_circular(DG1, **styles)

print(nx.info(DG1))

Z = nx.adjacency_matrix(DG1).todense().astype(int)
print('A: = ', np.diag(Z))
print(Z)
print('A*A: diag = ', np.diag(Z**1))
print(Z**2)
print('A*A*A = ', np.diag(Z**3))
print(Z**3)
print('A*A*A*A = ', np.diag(Z**4))
print(Z**4)
```

    Name: 
    Type: DiGraph
    Number of nodes: 9
    Number of edges: 27
    Average in degree:   3.0000
    Average out degree:   3.0000
    A: =  [0 0 0 0 1 0 0 0 0]
    [[0 0 0 1 0 0 1 1 1]
     [0 0 0 1 0 0 0 0 1]
     [0 0 0 0 0 0 1 0 1]
     [1 1 0 0 1 1 0 0 0]
     [0 0 0 1 1 0 0 1 0]
     [0 0 0 1 0 0 0 0 0]
     [1 0 1 0 0 0 0 1 0]
     [1 0 0 0 1 0 1 0 1]
     [1 1 1 0 0 0 0 1 0]]
    A*A: diag =  [0 0 0 0 1 0 0 0 0]
    [[4 2 2 0 2 1 1 2 1]
     [2 2 1 0 1 1 0 1 0]
     [2 1 2 0 0 0 0 2 0]
     [0 0 0 4 1 0 1 2 2]
     [2 1 0 1 3 1 1 1 1]
     [1 1 0 0 1 1 0 0 0]
     [1 0 0 1 1 0 3 1 3]
     [2 1 2 2 1 0 1 4 1]
     [1 0 0 2 1 0 3 1 4]]
    A*A*A =  [4 0 0 1 5 0 2 5 2]
    [[ 4  1  2  9  4  0  8  8 10]
     [ 1  0  0  6  2  0  4  3  6]
     [ 2  0  0  3  2  0  6  2  7]
     [ 9  6  3  1  7  4  2  4  2]
     [ 4  2  2  7  5  1  3  7  4]
     [ 0  0  0  4  1  0  1  2  2]
     [ 8  4  6  2  3  1  2  8  2]
     [ 8  3  2  4  7  2  8  5  9]
     [10  6  7  2  4  2  2  9  2]]
    A*A*A*A =  [35 12 13 26 19  4 22 32 32]
    [[35 19 18  9 21  9 14 26 15]
     [19 12 10  3 11  6  4 13  4]
     [18 10 13  4  7  3  4 17  4]
     [ 9  3  4 26 12  1 16 20 22]
     [21 11  7 12 19  7 13 16 15]
     [ 9  6  3  1  7  4  2  4  2]
     [14  4  4 16 13  2 22 15 26]
     [26 13 17 20 16  4 15 32 18]
     [15  4  4 22 15  2 26 18 32]]
    

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/a06e8fa2-02fb-0c6a-aeec-1e8c16919920.png)
    


#### 非循環サイクル
サイクルが存在しないサイクルを、非循環サイクルと呼ぶ。
非循環サイクルであれば、その隣接行列のすべての固有値はゼロであり、その逆も真である。


```python
import numpy.linalg as LA

# Graphオブジェクトの作成
DG2 = nx.DiGraph()

# nodeデータの追加
nodes3 = np.unique([pokemon1.ポケモン]+[pokemon1.図鑑単語])
edges3 = [(pokemon1.loc[i, 'ポケモン'], pokemon1.loc[i, '図鑑単語']) for i in range(len(pokemon1))]

DG2.add_nodes_from(nodes3)
DG2.add_edges_from(edges3)

plt.figure(figsize=(15,6))
plt.subplot(121)
nx.draw_circular(DG2, **styles)

plt.subplot(122)
nx.draw_circular(DG1, **styles)

Z = nx.adjacency_matrix(DG2).todense().astype(int)
w, v = LA.eig(Z)
print('cyclic: eigenvalues =', w)
print(Z)

ZA = nx.adjacency_matrix(DG1).todense().astype(int)
wa, va = LA.eig(ZA)
print('cyclic: eigenvalues =', wa)
print(ZA)
```

    cyclic: eigenvalues = [0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
     0.000000 0.000000]
    [[0 0 0 0 0 0 0 0 1 0]
     [0 0 0 0 0 0 1 1 0 0]
     [0 0 0 0 0 0 1 0 0 1]
     [0 0 0 0 0 0 0 1 1 0]
     [0 0 0 0 0 0 0 1 1 0]
     [0 0 0 0 0 0 0 0 0 1]
     [0 0 0 0 0 0 0 0 0 0]
     [0 0 0 0 0 0 0 0 0 0]
     [0 0 0 0 0 0 0 0 0 0]
     [0 0 0 0 0 0 0 0 0 0]]
    cyclic: eigenvalues = [3.353269 -2.546447 -1.996967 1.711009 1.098629 -0.840992 0.470910
     0.182422 -0.431834]
    [[0 0 0 1 0 0 1 1 1]
     [0 0 0 1 0 0 0 0 1]
     [0 0 0 0 0 0 1 0 1]
     [1 1 0 0 1 1 0 0 0]
     [0 0 0 1 1 0 0 1 0]
     [0 0 0 1 0 0 0 0 0]
     [1 0 1 0 0 0 0 1 0]
     [1 0 0 0 1 0 1 0 1]
     [1 1 1 0 0 0 0 1 0]]
    


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/e3d39f2e-ad9f-e6ff-2677-5b7431834ddf.png)
    


サイクルがない有向グラフはDAGと呼ばれる。DAGは隣接行列の行と列を入れ替えることで三角行列にすることができる。
無向でサイクルがないグラフは木や森と呼ばれる。木において頂点の数$n$、辺の数$m$とすると$n=m+1$となる。

#### 直径
グラフにおける直径とは、グラフの任意の2頂点間における最短パス長の中で、最大のもののことをいう。


```python
# ネットワークの可視化
plt.figure(figsize=(6,6))
nx.draw_circular(G1, node_size=1500, node_color='lightblue', with_labels=True, font_family='MS Gothic')

Z = nx.adjacency_matrix(G1).todense().astype(int)
print('shortest path between 句点 and 副詞的名詞:',nx.shortest_path(G1, "句点", '副詞的名詞'))
print('all shortest paths:', [p for p in nx.all_shortest_paths(G1, "句点", '副詞的名詞')])
print('shortest path length:', nx.shortest_path_length(G1, "句点", '副詞的名詞'))
print('diameter:', nx.diameter(G1))
```

    shortest path between 句点 and 副詞的名詞: ['句点', '動詞', 'サ変名詞', '格助詞', '副詞的名詞']
    all shortest paths: [['句点', '動詞', 'サ変名詞', '接続助詞', '副詞的名詞'], ['句点', '動詞', 'サ変名詞', '格助詞', '副詞的名詞'], ['句点', '動詞', '副詞', '格助詞', '副詞的名詞']]
    shortest path length: 4
    diameter: 4
    

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/52a05a74-6c4d-c92b-c3dd-be20ed01638a.png)


### 連結成分
#### 連結、連結成分
グラフ上の2頂点間のパスが存在するとき、そのグラフは連結であるという。
また、任意の2頂点間のパスが存在するような部分グラフを、そのグラフの連結成分と呼ぶ。
連結成分の個数は、グラフラプラシアンの固有値がゼロとなるものの個数で求められる。
グラフラプラシアンとは、グラフの隣接行列を$A$、各頂点の次数を対角成分に持つ対角ベクトルを$D$としたとき,$L=D-A$で求められる行列である。


```python
pokemon2 = pd.DataFrame({'ポケモン':['ピカチュウ','ピカチュウ','ライチュウ','ビードル','ビードル','メノクラゲ','メノクラゲ','ヤドラン','ヤドラン','パウワウ'],
                        '図鑑単語':['森','電気','電気','森','毒','海','毒','海','毒','海']})
pokemon2
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>ポケモン</th>
      <th>図鑑単語</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>ピカチュウ</td>
      <td>森</td>
    </tr>
    <tr>
      <th>1</th>
      <td>ピカチュウ</td>
      <td>電気</td>
    </tr>
    <tr>
      <th>2</th>
      <td>ライチュウ</td>
      <td>電気</td>
    </tr>
    <tr>
      <th>3</th>
      <td>ビードル</td>
      <td>森</td>
    </tr>
    <tr>
      <th>4</th>
      <td>ビードル</td>
      <td>毒</td>
    </tr>
    <tr>
      <th>5</th>
      <td>メノクラゲ</td>
      <td>海</td>
    </tr>
    <tr>
      <th>6</th>
      <td>メノクラゲ</td>
      <td>毒</td>
    </tr>
    <tr>
      <th>7</th>
      <td>ヤドラン</td>
      <td>海</td>
    </tr>
    <tr>
      <th>8</th>
      <td>ヤドラン</td>
      <td>毒</td>
    </tr>
    <tr>
      <th>9</th>
      <td>パウワウ</td>
      <td>海</td>
    </tr>
  </tbody>
</table>
</div>




```python
from networkx.algorithms import bipartite

B2 = nx.Graph()
B2.add_nodes_from(list(np.unique(pokemon2['ポケモン'])), bipartite=0)
B2.add_nodes_from(list(np.unique(pokemon2['図鑑単語'])), bipartite=1)
B2.add_edges_from([(pokemon2.loc[i, 'ポケモン'], pokemon2.loc[i, '図鑑単語']) for i in range(len(pokemon))])

plt.figure(figsize=(30,8))
plt.subplot(121)
nx.draw(B2, **styles)

print('connected')
print('number of connected components:', nx.number_connected_components(B2))
L = nx.laplacian_matrix(B2).todense().astype(int)
print('Laplacian matrix L:')
print(L)
np.set_printoptions(formatter={'float':'{:3f}'.format})
print('eigenvalues of L', LA.eigvals(L))

print('disconnected')
B2.remove_edges_from([('ビードル','毒')])

plt.subplot(122)
nx.draw(B2, **styles)

print('number of connected components:', nx.number_connected_components(B2))
L = nx.laplacian_matrix(B2).todense().astype(int)
print('Laplacian matrix L:')
print(L)
print('eigenvalues of L', LA.eigvals(L))
```

    connected
    number of connected components: 2
    Laplacian matrix L:
    [[ 1  0  0  0  0  0  0  0  0  0 -1  0]
     [ 0  2  0  0  0  0  0 -1 -1  0  0  0]
     [ 0  0  2  0  0  0  0 -1  0  0  0 -1]
     [ 0  0  0  1  0  0  0  0  0  0  0 -1]
     [ 0  0  0  0  2  0  0  0  0 -1 -1  0]
     [ 0  0  0  0  0  2 -1  0  0 -1  0  0]
     [ 0  0  0  0  0 -1  1  0  0  0  0  0]
     [ 0 -1 -1  0  0  0  0  2  0  0  0  0]
     [ 0 -1  0  0  0  0  0  0  1  0  0  0]
     [ 0  0  0  0 -1 -1  0  0  0  2  0  0]
     [-1  0  0  0 -1  0  0  0  0  0  2  0]
     [ 0  0 -1 -1  0  0  0  0  0  0  0  2]]
    eigenvalues of L [3.732051 3.000000 2.000000 1.000000 0.000000 0.267949 3.732051 3.000000
     2.000000 1.000000 -0.000000 0.267949]
    disconnected
    number of connected components: 2
    Laplacian matrix L:
    [[ 1  0  0  0  0  0  0  0  0  0 -1  0]
     [ 0  2  0  0  0  0  0 -1 -1  0  0  0]
     [ 0  0  2  0  0  0  0 -1  0  0  0 -1]
     [ 0  0  0  1  0  0  0  0  0  0  0 -1]
     [ 0  0  0  0  2  0  0  0  0 -1 -1  0]
     [ 0  0  0  0  0  2 -1  0  0 -1  0  0]
     [ 0  0  0  0  0 -1  1  0  0  0  0  0]
     [ 0 -1 -1  0  0  0  0  2  0  0  0  0]
     [ 0 -1  0  0  0  0  0  0  1  0  0  0]
     [ 0  0  0  0 -1 -1  0  0  0  2  0  0]
     [-1  0  0  0 -1  0  0  0  0  0  2  0]
     [ 0  0 -1 -1  0  0  0  0  0  0  0  2]]
    eigenvalues of L [3.732051 3.000000 2.000000 1.000000 0.000000 0.267949 3.732051 3.000000
     2.000000 1.000000 -0.000000 0.267949]
    

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/04206cca-cd20-8f33-a6af-ae2abed12bb1.png)



#### 強連結成分
有向グラフにおいて、任意の2頂点間のパスが存在するような部分グラフを、そのグラフの強連結成分(SCC)と呼ぶ。


```python
# ネットワークの可視化
plt.figure(figsize=(6,6))
nx.draw_circular(DG1, **styles)

print('number of strogly connected components:', nx.number_strongly_connected_components(DG1))
```

    number of strogly connected components: 1
    


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/f83a1dbb-c6b4-4205-4192-02c00f494564.png)
  


ある頂点$A$から到達可能な頂点集合を「頂点$A$のout-components」、
頂点$A$に到達可能な頂点の集合を「頂点$A$のin-components」と呼ぶ。
#### 連結性
2頂点間のパスの数を連結性と呼ぶ。


```python
for path in nx.all_simple_paths(DG1, "接続助詞", '動詞'):
    print(path)

print('shortest path length from 接続助詞 to 動詞:', nx.shortest_path_length(DG1, "接続助詞", '動詞'))
print('shortest path from 接続助詞 to 動詞:', nx.shortest_path(DG1, "接続助詞", '動詞'))
```

    ['接続助詞', '普通名詞', '格助詞', 'サ変名詞', '動詞']
    ['接続助詞', '普通名詞', '格助詞', '副詞', '動詞']
    ['接続助詞', '普通名詞', 'サ変名詞', '動詞']
    ['接続助詞', '普通名詞', 'サ変名詞', '格助詞', '副詞', '動詞']
    ['接続助詞', '普通名詞', '動詞性接尾辞', '動詞']
    ['接続助詞', 'サ変名詞', '動詞']
    ['接続助詞', 'サ変名詞', '普通名詞', '格助詞', '副詞', '動詞']
    ['接続助詞', 'サ変名詞', '普通名詞', '動詞性接尾辞', '動詞']
    ['接続助詞', 'サ変名詞', '格助詞', '普通名詞', '動詞性接尾辞', '動詞']
    ['接続助詞', 'サ変名詞', '格助詞', '副詞', '動詞']
    ['接続助詞', '副詞的名詞', '格助詞', '普通名詞', 'サ変名詞', '動詞']
    ['接続助詞', '副詞的名詞', '格助詞', '普通名詞', '動詞性接尾辞', '動詞']
    ['接続助詞', '副詞的名詞', '格助詞', 'サ変名詞', '動詞']
    ['接続助詞', '副詞的名詞', '格助詞', 'サ変名詞', '普通名詞', '動詞性接尾辞', '動詞']
    ['接続助詞', '副詞的名詞', '格助詞', '副詞', '動詞']
    shortest path length from 接続助詞 to 動詞: 2
    shortest path from 接続助詞 to 動詞: ['接続助詞', 'サ変名詞', '動詞']
    

2頂点間の辺カット集合とは、集合内の辺を除去することで、その2頂点間が非連結になるような集合を指す。
そのような集合で、最小のものを最小カットと呼ぶ。最大流とは、2頂点間のパスの数を指す。
最大流最小カットは、この両者が等しいことを示す定理であり、各辺の容量が異なっている場合でも成立する。


```python
from pprint import pprint

# Graphオブジェクトの作成
G1 = nx.Graph()
G1.add_nodes_from(nodes1)
G1.add_edges_from(edges1, capacity=1.0)

capa = nx.get_edge_attributes(G1, 'capacity')
print(capa)

plt.figure(figsize=(15,6))
plt.subplot(121)
nx.draw_circular(G1, **styles)

cut_deges = nx.algorithms.connectivity.minimum_edge_cut(G1, "動詞", "普通名詞")
print('cut size of 動詞 and 普通名詞', cut_deges)
flow_value, floe_dict = nx.algorithms.flow.maximum_flow(G1, "動詞", "普通名詞")
print('max flow of 動詞 and 普通名詞', flow_value)

print('edge 動詞-動詞性接尾辞 is deleted')
G.remove_edges_from([('動詞','動詞性接尾辞')])

plt.subplot(122)
nx.draw_circular(G1, **styles)

cut_deges = nx.algorithms.connectivity.minimum_edge_cut(G1, "動詞", "普通名詞")
print('cut size of 動詞 and 普通名詞', cut_deges)
flow_value, floe_dict = nx.algorithms.flow.maximum_flow(G1, "動詞", "普通名詞")
print('max flow of 動詞 and 普通名詞', flow_value)
```

    {('サ変名詞', '動詞'): 1.0, ('サ変名詞', '普通名詞'): 1.0, ('サ変名詞', '接続助詞'): 1.0, ('サ変名詞', '格助詞'): 1.0, ('副詞', '動詞'): 1.0, ('副詞', '格助詞'): 1.0, ('副詞的名詞', '格助詞'): 1.0, ('副詞的名詞', '接続助詞'): 1.0, ('動詞', '動詞性接尾辞'): 1.0, ('動詞', '句点'): 1.0, ('動詞性接尾辞', '動詞性接尾辞'): 1.0, ('動詞性接尾辞', '普通名詞'): 1.0, ('接続助詞', '普通名詞'): 1.0, ('普通名詞', '格助詞'): 1.0}
    cut size of 動詞 and 普通名詞 {('動詞', 'サ変名詞'), ('動詞性接尾辞', '普通名詞'), ('副詞', '格助詞')}
    max flow of 動詞 and 普通名詞 3.0
    edge 動詞-動詞性接尾辞 is deleted
    cut size of 動詞 and 普通名詞 {('動詞', 'サ変名詞'), ('動詞性接尾辞', '普通名詞'), ('副詞', '格助詞')}
    max flow of 動詞 and 普通名詞 3.0
    

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/aa5c033f-fd41-c8d2-c470-018e7edc5198.png)



### クラスタ係数
クラスタ係数は例えば「ある人の2人の友人が友人同士である割合」を表す。
#### local clustering coefficient
頂点$i$の次数を$k_i$としたとき、頂点$i$の友人のペア総数は$k_i(k_i-1)/2$あり、その中で辺で結ばれたものの割合を、頂点$i$に関するクラスタ係数$C_i$と呼ぶ。
頂点$i$の隣接頂点間のつながり具合を表す指標としてredundancy($R_i$)がある。これは頂点$i$の隣接頂点について、頂点$i$の他の隣接頂点と辺でつながっているものの数の平均である。
「頂点$i$の友人ペアの中で辺で結ばれたものの数」は$k_iR_i/2$であるから、$C_i$は次のように表せる。

```math
C_i=\frac{k_iR_i/2}{k_i(k_i-1)/2}=\frac{R_i}{k_i-1}
```

$C_i$をネットワークすべての頂点で平均を取った$C_{WS}=\frac{1}{n}\sum_i^nC_i$をネットワーク全体のクラスタ係数と呼ぶ。

完全グラフのクラスタ係数は1、2部グラフのクラスタ係数は0、ランダムグラフのクラスタ係数は0から1の間の数となる。


```python
d3 = pd.read_csv('co_occur.csv', encoding='shift-jis')

G3 = nx.Graph()
G3.add_edges_from([(d3.loc[i, '語1'], d3.loc[i, '語2']) for i in range(len(d3))], capacity=1.0)
G3.add_weighted_edges_from([(d3.loc[i, '語1'], d3.loc[i, '語2'], d3.loc[i, 'Freq']) for i in range(len(d1))])

plt.figure(figsize=(25,7))

plt.subplot(131)
nx.draw_circular(G3, **styles)

plt.subplot(132)
nx.draw_circular(B1, **styles)

plt.subplot(133)
nx.draw_circular(G1, **styles)

print('CC of complete graph', nx.clustering(G3, '文章'))
print('CC of bipartite graph', nx.clustering(B1, 'ピカチュウ'))
print('CC of random graph', nx.clustering(G1, '格助詞'))
```

    CC of complete graph 1.0
    CC of bipartite graph 0
    CC of random graph 0.16666666666666666
    


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/24f90ff1-1c69-9764-9fcb-f34d476f6053.png)

    


クラスタ係数のもう一つの定義は、「ネットワーク全体における長さ2のパス$N^{(2)}$のなかで、そのパスが閉路となるものの割合」である。
三角形の個数を$L_3$とすると、3頂点からなる三角形1つは、長さ2のパスで閉路となるもの6つに対応するので$C=\frac{6L_3}{|N^{(2)}|}$となる。

### 参考文献

https://www.amazon.co.jp/Python%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E5%88%86%E6%9E%90-Colaboratory%E3%81%A8NetworkX%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%E6%9D%91%E7%94%B0-%E5%89%9B%E5%BF%97/dp/4274224252/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1MGFJGT5I662K&keywords=python+%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF&qid=1647165563&s=books&sprefix=python+%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%2Cstripbooks%2C215&sr=1-1