どん底から這い上がるまでの記録

どん底から這い上がりたいけど這い上がれない人がいろいろ書くブログ(主にプログラミング)

pythonでigraphを使ってみる

 

前回書いた記事Windowsにigraphをインストールすることに成功しました。

www.pytry3g.com

 

今回は自分で用意したデータ(テキスト)を使って共起関係のグラフ(語と語のつながり)を表示させるところまでやってみます。

 

環境

実行環境です。

WindowsMeCabをインストールするには

https://www.pytry3g.com/entry/2018/04/24/143934#Windows

関連リンク

Tutorial

python-igraph manual

方針

今回は自分で用意したデータを使ってグラフを表示させます。そのため、まずデータを用意するところから軽く説明してそれからグラフの作り方について書いていきます。

データの用意

ロシアワールドカップが行われた期間にサッカー日本代表に関するツイートを集めることができたので、今回はそのデータを使ってグラフを生成させてみたいと思います。

www.pytry3g.com

 

データの中身はこんな感じです。

原口と槙野も先発だと期待してます。
槙野は出ると思うんですが、原口はもしかすると乾のサブかもねー。どちらにしても頑張ってー!
今日日本戦!!!たのしみ!頑張れ!コロンビア!
それ!もう日本出なくていい。経費の無駄
本田スタメン外したのは英断だけど中盤誰が守備すんねんw
このメンツなら原口だな。
コロンビアってサッカーつよいんですか
そうなんか今日日本と試合するんしょ?

グラフの生成

これからグラフを生成しますが、そのためにいくつかの前準備が必要になります。

順を追って説明していきます。

1. データを読み込む

集めたツイートが入ったファイルはpositive.txtという名前のテキストファイルになっています。これを改行区切りでcorpusというリストに入れます。

import codecs

with codecs.open('positive.txt', 'r', 'utf-8') as f:
    corpus = f.read().splitlines()

2. テキストを形態素解析する

リストcorpusに入っているすべてのテキストを形態素解析し名詞の基本形を取り出します。

import MeCab

tagger = MeCab.Tagger("-Ochasen")
def tokenizer(sentence):
    tag = tagger.parseToNode(sentence)
    tokens = []
    while tag:
        features = tag.feature.split(',')
        pos = features[0]
        token = features[-3]
        if token == "*":
            tag = tag.next
            continue
        if pos == '名詞':
            tokens.append(token)
        tag = tag.next
    return tokens if tokens else None

tokenslist = []
for sentence in corpus:
    tokens = tokenizer(sentence)
    assert tokens, "m9(^Д^)プギャー" # tokensがNoneならm9(^Д^)プギャー
    tokenslist.append(tokens)
print(corpus[0])
# 原口と槙野も先発だと期待してます。
print(tokenslist[0])
# ['原口', '槙野', '先発', '期待']

tokenslistはリストになっており、中身も文単位で単語の基本形が入ったリストになっています。

3. 共起リストを作る

pythonに標準で入っているitertoolsのcombinationsメソッドを使って、tokenslistから共起ペアを作っていきます。

combinations

itertools.combinations(iterable, r)

iterableな要素から長さrの重複なしの部分列(タプルで)返してくれます。

試しにtokenslistの一つの文を渡してみます。ペアを作るのでr = 2にしています。

from itertools import combinations
list(combinations(tokenslist[0], 2))

"""結果
[('原口', '槙野'),
 ('原口', '先発'),
 ('原口', '期待'),
 ('槙野', '先発'),
 ('槙野', '期待'),
 ('先発', '期待')]
"""

共起ペアを作る

リストco_occurに共起ペアを入れたリストを入れていく。

from itertools import combinations
co_occur = []
for tokens in tokenslist:
    if len(tokens) < 2:
        continue
    co_occur.append(list(combinations(tokens, 2)))
print(co_occur[0])
# [('原口', '槙野'), ('原口', '先発'), ('原口', '期待'), ('槙野', '先発'), ('槙野', '期待'), ('先発', '期待')]

 

co_occurはリストが入ったリストになっているので、これをフラットなリストにする。

co_occur_flatten = []
for pairs in co_occur:
    co_occur_flatten.extend(pairs)
print(co_occur_flatten[0])
# ('原口', '槙野')
print(len(co_occur_flatten))
# 19653

共起ペアをカウントする

共起ペアをカウントしてみます。

from collections import Counter
cnt = Counter(co_occur_flatten)

頻度が高いトップ10を見てみる。

print(*sorted(cnt.items(), key=lambda x: x[1], reverse=True)[:10], sep="\n")
"""Top10
(('日本', 'ん'), 26)
(('決勝', 'トーナメント'), 23)
(('日本', '代表'), 22)
(('コロンビア', '日本'), 21)
(('日本', '笑'), 21)
(('セネガル', 'コロンビア'), 21)
(('日本', '試合'), 20)
(('試合', 'ん'), 20)
(('セネガル', '戦'), 20)
(('日本', '日本'), 18)
"""

4. 共起辞書を作る

10回以上現れる共起ペアをグラフに表示させます。すべての共起ペアを表示させようとするとぐちゃぐちゃになってしまうので。

10回以上出現する共起ペアを辞書にしてみます。

co_occur_dict = {k: cnt[k] for k in cnt.keys() if cnt[k] >=10}
print(len(co_occur_dict))
# 73
print(list(co_occur_dict.items())[0])
# (('それ', '日本'), 11)

10回以上出現する共起ペアは全部で73個ありました。

5. 頂点の準備

今回作るグラフは共起関係(語と語のつながり)を表示させます。語と語のつながりを表示させるので頂点は語になります。

verticesには頂点となる語が入ります。

vertices = []
for pairs in co_occur_dict.keys():
    token1, token2 = pairs
    if token1 not in vertices:
        vertices.append(token1)
    if token2 not in vertices:
        vertices.append(token2)

print(len(vertices))
# 33

頂点(語)は全部で33個になりました。

6. 辺の準備

頂点から辺を生成します。

下のプログラムは共起ペア (['単語1', '単語2']) から単語IDのペア([単語1のID, 単語2のID])に変換しています。

edges = [(vertices.index(pairs[0]), vertices.index(pairs[1])) for pairs in co_occur_dict.keys()]
print(edges[0])
# (0, 1)

7. グラフを作る

ここでようやくグラフを作ります。

from igraph import Graph
g = Graph(
    vertex_attrs={"label": vertices}, # 頂点についての設定
    edges=edges, # 辺の設定、数値ペアのリストを渡す (例)[(0, 1),(1, 2),(0, 2)]
    directed=False
)

igraphのGraph()に頂点のラベル(単語)と辺(単語IDのペア)を渡してグラフを生成しています。

8. グラフの表示

igraphのplotでグラフを表示します。

from igraph import plot
plot(
    g,
    vertex_size=25, # 頂点の大きさ
    bbox=(600, 600), # グラフの大きさ
    vertex_color="skyblue" # 頂点の色
)

結果

グラフを表示させるとこんな感じになりました。

f:id:pytry3g:20180829210727p:plain

ロシアワールドカップの期間中に集めたツイートを使ってこの共起グラフを生成してみましたが、このグラフを見てみるとやはり日本代表についてや他の国に関するツイートがあることが分かります。

 

それからグラフでも大迫はやっぱり半端なかった。

ソースコード

コードをまとめると以下のようになります。

MeCabとigraphがインストールされていれば以下のコードをコピペで実行できるようしています。

import MeCab
import codecs
from collections import Counter
from igraph import Graph, plot
from itertools import combinations

# データの読み込み
# サンプルデータ
corpus = ["セネガルつええ、ボルト三体くらいいるわ笑笑",
          "しょーみコロンビアより強い",
          "それなまちがいないわ"]

# 形態素解析
tagger = MeCab.Tagger("-Ochasen")
def tokenizer(sentence):
    tag = tagger.parseToNode(sentence)
    tokens = []
    while tag:
        features = tag.feature.split(',')
        pos = features[0]
        token = features[-3]
        if token == "*":
            tag = tag.next
            continue
        if pos == '名詞':
            tokens.append(token)
        tag = tag.next
    return tokens if tokens else None

tokenslist = [] # 単語のリストを入れる
for sentence in corpus:
    tokens = tokenizer(sentence)
    assert tokens, "m9(^Д^)プギャー" # tokensがNoneならm9(^Д^)プギャー
    tokenslist.append(tokens)

co_occur = [] # 共起ペアを作る
for tokens in tokenslist:
    if len(tokens) < 2:
        continue
    co_occur.append(list(combinations(tokens, 2)))

co_occur_flatten = [] # 共起ペアをフラットにする
for pairs in co_occur:
    co_occur_flatten.extend(pairs)

# 共起ペアをカウントする。
cnt = Counter(co_occur_flatten)
# 10回以上出現する共起ペアを辞書に登録
co_occur_dict = {k: cnt[k] for k in cnt.keys() if cnt[k] >=10}

# 頂点となる単語のリストを生成
vertices = []
for pairs in co_occur_dict.keys():
    token1, token2 = pairs
    if token1 not in vertices:
        vertices.append(token1)
    if token2 not in vertices:
        vertices.append(token2)

# 辺の生成、中身は単語IDのペアになっている。
edges = [(vertices.index(pairs[0]), vertices.index(pairs[1])) for pairs in co_occur_dict.keys()]

# グラフの生成
g = Graph(
    vertex_attrs={"label": vertices}, # 頂点についての設定
    edges=edges, # 辺の設定、数値ペアのリストを渡す (例)[(0, 1),(1, 2),(0, 2)]
    directed=False
)

# グラフの表示
plot(
    g,
    vertex_size=25, # 頂点の大きさ
    bbox=(600, 600), # グラフの大きさ
    vertex_color="skyblue" # 頂点の色
)