0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ネットワークのクラスタリング - Louvain - 備忘録

Last updated at Posted at 2021-08-14

#概要
コミュニティのクラスタリングがいずれ仕事にも使えそうなので、お試ししてみた備忘録を残す。

  • 実施期間: 2021年8月
  • 環境:Ubuntu20.04 LTS
  • パケージ:python-louvain, networkxなど

##1. 準備
パケージをインストールする。他はインストール済みとする。

pip install python-louvain

今回使用するパケージをすべてimportする。
また、お試しのデータセットはのはkarateを使用する。
クラスタリングするときゴミノードをremoveするのでG_clsにグラフをコピー(深い)しておく

import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import community
import collections
from collections import Counter
from sklearn import preprocessing, metrics

G = nx.karate_club_graph()
G_cls = G.copy()  # クラスタリング結果用
print(nx.info(G))

# GraphRicciCurvatureからお借りしたhelper関数
# clustering_labelに対し、自動で色付けしてくださる。
def draw_graph(G, clustering_label="club"):
    """
    A helper function to draw a nx graph with community.
    """
    complex_list = nx.get_node_attributes(G, clustering_label)

    le = preprocessing.LabelEncoder()
    node_color = le.fit_transform(list(complex_list.values()))

    # nx.draw_spring(G, nodelist=G.nodes(),
    nx.draw_circular(G, nodelist=G.nodes(),
                   node_color=node_color,
                   cmap=plt.cm.rainbow,
                   alpha=0.8, with_labels=True)

# defaultで入っている属性club('Mr. Hi', 'Officer')で色分け
draw_graph(G, "club")

Name: Zachary's Karate Club
Type: Graph
Number of nodes: 34
Number of edges: 78
Average degree: 4.5882

Screenshot from 2021-08-28 15-20-51.png

##2. Centrality
ネットワークにおいてどのnodeが重要なのかcentralityという指標で表すことができる。文字通り中心nodeを探すわけである。
コミュニティを解析するのであれば、eigenvector_centralityやpagerankといった自分につながっているnodesの重要性も考慮した指標がいいように思う。
netowkxに実装されているcentralityは下記を参照。全部で30個近くあるのでよく調べて使うこと。

df = pd.DataFrame(columns=[i for i in range(len(G.nodes))])
df.loc['degree_centrality'] = [v for k, v in nx.algorithms.centrality.degree_centrality(G).items()]
df.loc['eigenvector_centrality'] = [v for k, v in nx.algorithms.centrality.eigenvector_centrality(G).items()]
df.loc['betweenness_centrality'] = [v for k, v in nx.algorithms.centrality.betweenness_centrality(G).items()]
df.loc['load_centrality'] = [v for k, v in nx.algorithms.centrality.load_centrality(G).items()]
df.loc['pagerank_scipy'] = [v for k, v in nx.pagerank_scipy(G, alpha=0.9).items()]

node=0, 33がどのcentralityでも上昇しているのは、karateにおいて両者が'Mr. Hi'、'Officer'の中心人物であることを示している。いわゆるインフルエンサーか。

df_t = df.T
df_t.plot()
# print(df_t.describe())

Screenshot from 2021-08-29 09-09-18.png

##3. Louvainでクラスタリング
Louvainのアルゴリズムは各nodesの末端から距離が近いものどうしをグルーピングする手法で、デンドログラムを描く手法
理屈を知りたければいつもお世話になっているProf. Leskovecのレクチャー拝聴のこと。どの講義もわかりやすく助かる。

今回使用するパケージであるcommunityのドキュメントはCommunity detection for NetworkX’s documentation参照
この"community"はpython-louvainパケージをインストールすると使えるようになる。
APIの使い方はcommunity API参照

流れは、
第1層目(引数:1)のデンドロをfirstPartitionに描いてもらい、
この中で小さなクラスタに属しているゴミnodesをremoveする
(計算処理に使うのはG_clsの方である点、注意)
その後再度クラスタリング(best_partition())して精度を上げる
なおremoveしたnodesも表示したいので、表示には元のGを使用する。
新しく"cls"属性をGに追加しそこにクラスタを色で登録したが、draw_graph()で描画するのであればこの色は使われない。

# ゴミnodesの削除
dendrogram = community.generate_dendrogram(G_cls)
firstPartition = community.partition_at_level(dendrogram, 1)

minCommSize=6
sparseComm = set([k for k,v in Counter(firstPartition.values()).items() if v<minCommSize])
nodes = [node for node in G_cls.nodes() if firstPartition[node] in sparseComm]
print(sparseComm)
print(f'removed: {nodes}')

G_cls.remove_nodes_from(nodes)
for i in range(len(nodes)):
    # 表示にはremoveしたnodeも灰色で表示したいのでGを使う
    G.nodes[nodes[i]]['cls'] = -1   # gray

# 本番クラスタリング
partition = community.best_partition(G_cls, resolution=0.8, random_state=30)

# 'cls'属性に色を登録(ちょっとダサい) 描画用なのでGの方
k_lst = list(partition.keys())
v_lst = list(partition.values())
color = {-1: 'gray', 0: 'r', 1: 'g', 2:'b', 3:'c', 4:'m', 5:'y'}

clr = []
for i in range(len(k_lst)):
    ii = k_lst[i]
    G.nodes[ii]['cls'] = v_lst[i]
    clr.append(color[v_lst[i]])

# 属性を確認
for i in range(len(G.nodes)):
    print(f'{i}: {G.nodes[i]}')

# 描画
draw_graph(G, "cls")
# nx.draw_networkx(G,
#                  node_size=300,
#                  node_color=clr, with_labels=True
#                  )

removed: [4, 5, 6, 10, 16]

0: {'club': 'Mr. Hi', 'cls': 1}
1: {'club': 'Mr. Hi', 'cls': 1}
2: {'club': 'Mr. Hi', 'cls': 1}
3: {'club': 'Mr. Hi', 'cls': 1}
4: {'club': 'Mr. Hi', 'cls': -1} 以下略

Screenshot from 2021-08-28 16-08-15.png

紫がremoveされたnodesとなる。その他のnodesは3クラスに分けられたが、ground-truthの'club'にとても一致していない。

そこでremoveしないようにminCommSize=0、デンドロの深さはbest_partition()のresolutionで調整するのでこれを1.5に変更
再度描画すると下図となる。1.準備で描いたものと比べ、node 9をnode 8にミスジャッジしている程度で、その他は正解となった。
ネットワークの規模がとても小さいのでそもそも前処理でnode削除など必要なかった。
でも規模が数千、数万nodesになったときには必要な処理と思われるので、決して無駄ではない。

Screenshot from 2021-08-28 16-14-02.png

次回はGraphRicciCurvatureでkarateをクラスタリングする。

以上

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?