networkXとは
NetworkXはネットワーク(グラフ)を構築し、その上でいろいろな計算を行うことができるツールです。
主な機能として、グラフの作成と操作(ノードとエッジを使ってネットワークを構築する)、ネットワーク分析(ノードやエッジの中心性、クラスタリング係数、最短経路などの計算)ができるので、データサイエンスや機械学習、社会ネットワーク分析など、幅広い分野で利用されています。
仕事で必要になったので勉強した記録をアウトプットしていきます。
乃木撮について
乃木撮は、乃木坂46のメンバーが互いのオフショットを撮影した写真集です。2016年12月から2018年4月までの期間に撮影された610枚の写真を収めた252ページの写真集です。現在はVol.03まで発売されています。
企画のコンセプトからすると、グループ内の人間関係をある程度は反映しているものと言ってもよいのではないでしょうか。
勉強するためには自分が興味を持てる題材でするべきということで、実際楽しい時間を過ごせました。
あと、ネットを調べた限りでは同じようなコンセプトで分析されている記事はないようです。
写真から得られる情報
まず、1枚の写真につき、撮影したメンバーと写っているメンバーのデータを取り出します。
例えば、下のような写真があった場合はこのように表現することにします。
photo = ('秋元真夏', '西野七瀬', '白石麻衣')
最初に撮影者の名前を入れて、ついで被写体の名前を入れました。被写体は複数の可能性もありますし、また自撮りのこともあります。
photos_01 = [
('新内眞衣', '齋藤飛鳥'), # 01-001
('若月佑美', '白石麻衣', '衛藤美彩'), # 01-002
('斉藤優里', '西野七瀬'), # 01-003
('堀未央奈', '衛藤美彩', '秋元真夏'), # 01-004
('秋元真夏', '生田絵梨花'), # 01-005
('秋元真夏', '白石麻衣'), # 01-006
('伊藤万理華', '星野みなみ', '西野七瀬', '若月佑美', '松村沙友理'), # 01-007
('伊藤万理華', '松村沙友理'), # 01-008
('斉藤優里', '生田絵梨花'), # 01-009
('伊藤万理華', '西野七瀬', '衛藤美彩', '高山一実', '秋元真夏'), # 01-010
.
.
.
(中略)
]
実際に作成したデータの一部です。
写真にはそれぞれ、撮影者の名前と写真撮影時のエピソードや被写体へのコメントなどが書いてあります。写真に写っている人の名前も明記されているので、おそらく画像認識でこの程度のデータはかなり正確に作成できるのでしょうが、今回は人力でしました。いずれ画像認識の勉強をすることがあれば挑戦したいです。
リストの名前がphotos_01とあるのは、当然Vol.02、Vol.03も扱う予定だからです。
なお、名前を入力する際には、「サイトウさん」に注意が必要です。'齋藤飛鳥', '斉藤優里', '斎藤ちはる'の全てで異なっています。
ノードの作成
photos_01からメンバーひとりひとりの名前を取り出してノードにします。
persons = set() # setにして重複を避ける
for photo in photos_01:
for person in photo:
persons.add(person)
print(persons)
# {'伊藤万理華', '斎藤ちはる', '阪口珠美', '山下美月', '岩本蓮加', '佐々木琴子', '和田まあや', '桜井玲香', '大園桃子', '中田花奈', '西野七瀬', '梅澤美波', '伊藤純奈', '山崎怜奈', '鈴木絢音', '吉田綾乃', '中元日芽香', '橋本奈々未', '秋元真夏', '星野みなみ', '久保史緒里', '伊藤かりん', '与田祐希', '佐藤楓', '寺田蘭世', '堀未央奈', '新内眞衣', '川後陽菜', '能條愛未', '向井葉月', '松村沙友理', '高山一実', '白石麻衣', '渡辺みり愛', '中村麗乃', '若月佑美', '衛藤美彩', '井上小百合', '伊藤理々杏', '相楽伊織', '川村真洋', '生田絵梨花', '北野日奈子', '樋口日奈', '齋藤飛鳥', '斉藤優里', '生駒里奈'}
print(len(persons)
# 47
無事47人分のノードが作成できています。
エッジの作成
それぞれの写真について、撮影者と被写体、被写体が複数の場合は被写体同士にも繋がりがあるとしてエッジを作成します。
先ほどの写真を例として使うと、撮影者「秋元真夏」と被写体2人「西野七瀬」「白石麻衣」の計3人から2人を選びます。この場合、intertools.combinationsを利用します。第一引数にリストまたは集合、第二引数に選択する個数を渡すと、その組み合わせを返してくれます。
import itertools
photo = ('秋元真夏', '西野七瀬', '白石麻衣')
connects = list(itertools.combinations(set(photo), 2))
print(connects)
# [('秋元真夏', '西野七瀬'), ('秋元真夏', '白石麻衣'), ('西野七瀬', '白石麻衣')]
これを全ての写真で行います。とりあえず繋がりがあるかどうかを知りたいので、connectionsという集合としています(次回、出現回数のデータも考慮します)。
また、自撮りの場合があって、1枚の写真のデータに撮影者と被写体で同じ名前が入っていることがあるので、set(photo)として重複を省きます。
connections = set()
for photo in photos_01:
connects = set(itertools.combinations(set(photo), 2))
for connect in connects:
connections.add(connect)
print(len(connections))
# 464
464本ものエッジが作成されるようです。
networkXを使ってネットワークを表示
import networkx as nx
import matplotlib.pyplot as plt
# 無向グラフの作成
G = nx.Graph()
# ノードの追加
G.add_nodes_from(persons)
# エッジの追加
G.add_edges_from(connections)
# グラフの可視化
nx.draw(G,
pos=nx.spring_layout(G),
with_labels=True,
)
ようやく完成、と思いきや、残念ながら名前が豆腐化しています。
matplotlibはデフォルトでは日本語表示に対応していないので、フォントを導入しないといけないようです。
日本語フォント導入
いろいろな方法があるようですが、google colabの場合だと、フォントデータをgoogleドライブに置くのが簡単でした。
IPAexフォントをダウンロードして、解凍してできたipaexg.ttfというファイルをgoogleドライブに置き、その上で下記コードによってメンバーの名前を日本語表示できるようになりました。
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import font_manager
font_manager.fontManager.addfont('/content/drive/MyDrive/ipaexg.ttf')
matplotlib.rc('font', family='IPAexGothic')
# グラフがギュッと詰まっていて見にくいので大きな図にする
plt.figure(figsize=(20, 15))
nx.draw(G,
pos=nx.spring_layout(G), # よい感じにレイアウトしてくれる
with_labels=True,
font_family="IPAexGothic", # 日本語表示のためフォントを指定する必要がある
font_size=20, # フォントの大きさ
alpha=0.3 # 透過性
)
plt.show()
とりあえず完成。
1期生の中心メンバーが真ん中に集まって密な繋がりを形成しているように見えます。
大まかに期別でまとまっているようにも見えます。
感想
上のグラフでも、ある程度の情報は読み取れますが、ちょっと見にくすぎますし、せっかくなのでもっと工夫したいところがあります。まず、写っている回数によってノードの大きさを変えたいし、繋がり強いほど線を太くして関係性を強調したい。あと、期別で色を変えたり、関係性の強さでクラスタリングしたり、というのは次回の記事で。