テキストに含まれた情報を有向グラフに変換する話（一）：可視化の章

こんにちは。理系大学院で修業中のスーパーケロケロです。趣味で自然言語解析（NLP）の勉強をしています。最近、テキストに含まれた情報を有向グラフに変換するPythonライブラリー[`naruhodo`](https://github.com/superkerokero/naruhodo)を作ったので、ライブラリーの紹介も兼ねて、テキストを有向グラフに変換する話を少ししてみたいと思います。

`naruhodo`のGithubリポジトリは[**こちら**](https://github.com/superkerokero/naruhodo)、最新バージョンは[![PyPI version](https://badge.fury.io/py/naruhodo.svg)](https://badge.fury.io/py/naruhodo)です。

## 自然言語解析の流れ

自然言語解析を料理に例えれば、入力されたテキストは収穫待ちのコムギのようで、そのままでは使えない。このコムギを形態素解析で脱殻し（形態素単位で分離）、さらに词类（Part-Of-Speech）や依存構造解析で小麦粉にしてから（文法情報の付与）、ようやくパンのような美味しい食べ物が作れる（実際の応用）。

![Flowchart_small.png](https://qiita-image-store.s3.amazonaws.com/0/243355/529c4873-eb45-a22c-ce41-f1469a0ddbbb.png)

**テキストが処理されるごとに、使える情報が増えて、応用の幅が広げるわけです。**

## 文＝＞木、文章＝＞有向グラフ

 例えば、テキストに依存構造解析を施すと、下の図のように、依存文法の情報が単語間の繋がりとして付与される。

>**依存構造**ーー
>文の構文構造の表現形式のひとつ。文内の単語間の依存関係(係り受け関係)の集合によって文の構文構造を表現する。日本語では文節の係り受け関係を依存構造と呼ぶこともある。英語と日本語の依存構造の例を以下に示す。単語もしくは文節の依存関係を表わす矢印の向きは、通常は係り先から係り元の方向であるが、日本語では逆に係り元から係り先の方向であることが多い。ーー[*JAIST言語情報処理ポータル*](http://www.jaist.ac.jp/project/NLP_Portal/doc/glossary/)

![dependency.png](https://qiita-image-store.s3.amazonaws.com/0/243355/cb8777ea-4066-d3dc-5e1f-24b327964fab.png)

![dependency_ja.png](https://qiita-image-store.s3.amazonaws.com/0/243355/fd241816-2759-6d27-cc31-b7c575c2e1b0.png)

日本語依存構造解析ツール[Cabocha](https://taku910.github.io/cabocha/)の典型的な出力はこのようになっている。

```
* 0 3D 0/1 -1.591231
私	名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
* 1 3D 0/1 -1.591231
望遠鏡	名詞,一般,*,*,*,*,望遠鏡,ボウエンキョウ,ボーエンキョー
で	助詞,格助詞,一般,*,*,*,で,デ,デ
* 2 3D 0/1 -1.591231
少女	名詞,一般,*,*,*,*,少女,ショウジョ,ショージョ
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
* 3 -1D 0/1 0.000000
見	動詞,自立,*,*,一段,連用形,見る,ミ,ミ
た	助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
```

文はチャンクごとに分離され、依存構造はチャンクのIDで表されています。例えば、IDが0の”私は”はIDが3の”見た”に依存している事を”3D”で表示します。それぞれの文は必ず一つのROOTが存在するので（”‐1D”のチャンク）、典型的な[木構造](https://ja.wikipedia.org/wiki/%E6%9C%A8_(%E6%95%B0%E5%AD%A6))（もっと正確に言うと有向根付き木）になっています。

当然、複数の文から複数の木が得られます。木の間にもし共通するノードがあれば、繋がるようになります。**このような木の連合は結果的に有向グラフになります。**

さらにノードの出現回数や词类に応じて、ノードのサイズやカラーを調整すれば、きれいなグラフになるのではないか？こういったグラフは[Word Cloud](https://qiita.com/kenmatsu4/items/9b6ac74f831443d29074)なんかよりずっと情報量が多く、可視化したらめちゃくちゃ**格好いい**に違いない。＝＞これが`naruhodo`を作るきっかけになった。

という事で、早速`naruhodo`でこういうグラフを可視化してみようではないか。

>**注1**：グラフ理論に詳しい人なら、木の集合を言うとまず森だと認識するかも知れませんが、グラフ理論で言う森は"閉路を持たない（連結であるとは限らない）無向グラフ"なので、前述の"共通するノードで繋がる（かも知れない）有向根付き木の集合"とは違うものになっています。更に共通するノードで繋がる事を許すと、閉路を形成する可能性もあるので、有向非巡回グラフでもありません。

>**注2**：依存構造解析の結果は有向根付き木であることは間違いありません。ただ、`naruhodo`のように可視化する時、文の中に同じノードが複数回出現する場合は一つのノードに合併するようにしています。ノードの合併によって、各ノードからROOTまでは複数のパスが存在できる。この場合、有向根付き木の"各ノードからROOTまでに唯一のパスが存在する"という性質が満たされなくなるので、生成されたグラフは厳格な有向根付き木でもなく、単純な連結有向グラフである。でもこのグラフは唯一のROOTを持っている事や階層構造を持っているなど、木の特徴がまだ一部残っているので、複数の文から生成されるグラフと区分するために、ここではとにかく木と呼ぶことにします。

## 依存構造グラフの可視化

`naruhodo`は`MeCab`と`Cabocha`で形態素解析と依存構造解析を行います、だからまずは[**こちらの記事**](https://qiita.com/january108/items/85c80769ea870c190eaa)を参照して`MeCab`と`Cabocha`をインストールしましょう。

`MeCab`と`Cabocha`がインストールできたら、`pip`で`naruhodo`をインストールします。

```
pip install naruhodo
```

`naruhodo`をインストールできたら、まずはパーサを取得します。

```python
from naruhodo import parser
dp = parser(gtype="d")
```

ここの`gtype`はグラフの種類のことで、今は依存構造のグラフを生成したいので、`”d”`に指定します。

準備が整えたので、`add`でテキストをグラフに追加します。

```python
dp.add("私は望遠鏡で少女を見た。")
```

よし、それではこの文で生成した木構造を確認しましょう。
Jupyter Notebookを使っているならば、`show`で図を見れます。

```python
dp.show()
```

それ以外の場合は、`plotToFile`で図を出力します。

```python
dp.plotToFile(filename="pic.png")
```

こういう図が生成された。ちゃんとイイ感じに依存構造を表しています。

![dpic1.png](https://qiita-image-store.s3.amazonaws.com/0/243355/9015754b-5213-6ebe-2499-85a002c9ca5b.png)

それでは、共通している部分を含む新しい文を追加してみよう。

```python
dp.add("見た少女は美人だった。")
dp.show()
```

すると、こういうグラフになります。

![dpic2.png](https://qiita-image-store.s3.amazonaws.com/0/243355/bb221c46-9b45-f402-4670-7a214989ef98.png)

うん、ストーカーがいる事がグラフからでもよく分かりそうです。( o∀o)

よし、もう少し複雑なテキストを投げてみよう。

```python
# まずはグラフをリセットします
dp.reset()
#　文を順番に沿って追加
dp.add("田中一郎は田中次郎が描いた絵を田中三郎に贈った。")
dp.add("三郎はこの絵を持って市場に行った。")
dp.add("市場には人がいっぱいだ。")
#　図を表示
dp.show()
```

![dpic3.png](https://qiita-image-store.s3.amazonaws.com/0/243355/bea4eb1e-b35f-13bb-65c6-97fd9a7f8f36.png)

おお、イイ感じではないか？
それではようやくメインディッシュのニュースをグラフ化にしましょう。

今回は[〔マーケットアイ〕外為：ドル105円半ばで小動き、実需筋の売り買い交錯](https://jp.reuters.com/article/tokyo-frx-idJPL3N1RA2SG)というニュースでグラフを生成してみます。

こういった試行錯誤を便利にできるため、`naruhodo`に`addUrls`というウェブページからテキストを取ってくる機能も実装しました。指定されたウェブページから`<p>`タッグに含まれる内容を集めるという簡単なスクレーピング機能です。

```python
# まずはグラフをリセットします
dp.reset()
# ウェブページのUrlを追加します。複数のUrlがある場合、Urlのリストを渡してもオッケイです
dp.addUrls("https://jp.reuters.com/article/tokyo-frx-idJPL3N1RA2SG")
#　図を表示
dp.show()
```
![dpic4_small.png](https://qiita-image-store.s3.amazonaws.com/0/243355/c8d0b5d7-46f9-3d6a-ebdb-a9e42377c7d1.png)

......

............

..................

うん、なにこれ（・∀・）

なんか使えそうだけどまだこれだけでは何も分からないグラフです。
次回の[依存構造グラフの章](https://qiita.com/superkerokero/items/562de7af26e4814038b6)でこのグラフに含まれる情報について考察してみます。

<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="クリエイティブ・コモンズ・ライセンス" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />この 作品 は <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンス</a>の下に提供されています。