COTOHA API:構文解析の出力結果をグラフ化してみた

# はじめに

[COTOHA API](https://api.ce-cotoha.com/)の構文解析結果について、pythonライブラリを用いて整形・グラフ化した。

# COTOHA APIの構文解析

COTOHA APIの構文解析出力結果は下記のようなJSONデータである。

### 入出力
入力文「公園から、望遠鏡で星を見ている女の子を見た」
<details><summary>出力結果(クリックして開いてください)</summary><div>

```json

{
  "result": [
    {
      "chunk_info": {
        "id": 0,
        "head": 5,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": []
      },
      "tokens": [
        {
          "id": 0,
          "form": "公園",
          "kana": "コウエン",
          "lemma": "公園",
          "pos": "名詞",
          "features": [],
          "dependency_labels": [
            {
              "token_id": 1,
              "label": "case"
            },
            {
              "token_id": 2,
              "label": "punct"
            }
          ],
          "attributes": {}
        },
        {
          "id": 1,
          "form": "から",
          "kana": "カラ",
          "lemma": "から",
          "pos": "格助詞",
          "features": [
            "連用"
          ],
          "attributes": {}
        },
        {
          "id": 2,
          "form": "、",
          "kana": "",
          "lemma": "、",
          "pos": "読点",
          "features": [],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 1,
        "head": 3,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": []
      },
      "tokens": [
        {
          "id": 3,
          "form": "望遠鏡",
          "kana": "ボウエンキョウ",
          "lemma": "望遠鏡",
          "pos": "名詞",
          "features": [],
          "dependency_labels": [
            {
              "token_id": 4,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 4,
          "form": "で",
          "kana": "デ",
          "lemma": "で",
          "pos": "格助詞",
          "features": [
            "連用"
          ],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 2,
        "head": 3,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": []
      },
      "tokens": [
        {
          "id": 5,
          "form": "星",
          "kana": "ホシ",
          "lemma": "星",
          "pos": "名詞",
          "features": [],
          "dependency_labels": [
            {
              "token_id": 6,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 6,
          "form": "を",
          "kana": "ヲ",
          "lemma": "を",
          "pos": "格助詞",
          "features": [
            "連用"
          ],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 3,
        "head": 4,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 3,
        "links": [
          {
            "link": 1,
            "label": "implement"
          },
          {
            "link": 2,
            "label": "object"
          }
        ],
        "predicate": [
          "past"
        ]
      },
      "tokens": [
        {
          "id": 7,
          "form": "見",
          "kana": "ミ",
          "lemma": "見る",
          "pos": "動詞語幹",
          "features": [
            "A"
          ],
          "dependency_labels": [
            {
              "token_id": 3,
              "label": "iobj"
            },
            {
              "token_id": 5,
              "label": "dobj"
            },
            {
              "token_id": 8,
              "label": "aux"
            },
            {
              "token_id": 9,
              "label": "aux"
            },
            {
              "token_id": 10,
              "label": "aux"
            }
          ],
          "attributes": {}
        },
        {
          "id": 8,
          "form": "て",
          "kana": "テ",
          "lemma": "て",
          "pos": "動詞接尾辞",
          "features": [
            "接続",
            "連用"
          ],
          "attributes": {}
        },
        {
          "id": 9,
          "form": "い",
          "kana": "イ",
          "lemma": "いる",
          "pos": "動詞語幹",
          "features": [
            "A",
            "Lて連用"
          ],
          "attributes": {}
        },
        {
          "id": 10,
          "form": "る",
          "kana": "ル",
          "lemma": "る",
          "pos": "動詞接尾辞",
          "features": [
            "連体"
          ],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 4,
        "head": 5,
        "dep": "D",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": [
          {
            "link": 3,
            "label": "adjectivals"
          }
        ]
      },
      "tokens": [
        {
          "id": 11,
          "form": "女の子",
          "kana": "オンナノコ",
          "lemma": "女の子",
          "pos": "名詞",
          "features": [],
          "dependency_labels": [
            {
              "token_id": 7,
              "label": "acl"
            },
            {
              "token_id": 12,
              "label": "case"
            }
          ],
          "attributes": {}
        },
        {
          "id": 12,
          "form": "を",
          "kana": "ヲ",
          "lemma": "を",
          "pos": "格助詞",
          "features": [
            "連用"
          ],
          "attributes": {}
        }
      ]
    },
    {
      "chunk_info": {
        "id": 5,
        "head": -1,
        "dep": "O",
        "chunk_head": 0,
        "chunk_func": 1,
        "links": [
          {
            "link": 0,
            "label": "source"
          },
          {
            "link": 4,
            "label": "object"
          }
        ],
        "predicate": [
          "past"
        ]
      },
      "tokens": [
        {
          "id": 13,
          "form": "見",
          "kana": "ミ",
          "lemma": "見る",
          "pos": "動詞語幹",
          "features": [
            "A"
          ],
          "dependency_labels": [
            {
              "token_id": 0,
              "label": "nmod"
            },
            {
              "token_id": 11,
              "label": "dobj"
            },
            {
              "token_id": 14,
              "label": "aux"
            }
          ],
          "attributes": {}
        },
        {
          "id": 14,
          "form": "た",
          "kana": "タ",
          "lemma": "た",
          "pos": "動詞接尾辞",
          "features": [
            "終止"
          ],
          "attributes": {}
        }
      ]
    }
  ],
  "status": 0,
  "message": ""
}
```

</div></details>

### 出力結果について

文はチャンクごとに分解されチャンクIDで表現されている。チャンク間の依存関係はそのチャンクIDを用いて`links`に記述されており、例えばIDが5の`見た`は、IDが0の`公園から`に係っている事がわかる。
また依存関係には深層格として利用できる意味関係ラベルが付与されており、本例では意味関係ラベル`source`が付与されている。

* [意味関係ラベル一覧](https://api.ce-cotoha.com/contents/reference-detail.html#link-list)

さらに、各チャンクはトークン(形態素)ごとに分解され、トークン間の依存関係についても`dependency_labels`に記述されている。

## (若干)文量が…長い

…とはいえ情報量がすこし大きいため、ちょっとした解析やひと目で出力を確認したいときに扱いづらい。

ここでは、係り受け関係がひと目でわかる形式を出力するスクリプトを共有する。

### 動作環境

python3.6.2

### 係り受け情報を抽出する

まず係り受け情報を抽出する

```python

def extract_dependency_info(jsonfile):
    chunkid_text_dict = dict()
    dependency_info = list()

    # 解析結果(json)から係り受け情報を抽出
    for chunk in jsonfile["result"]:
        chunk_id = chunk["chunk_info"]["id"]
        tokens = [token["form"] for token in chunk["tokens"]]
        chunkid_text_dict[chunk_id] = " ".join(tokens)
        for link in chunk["chunk_info"]["links"]:
            dependency_info.append([chunk_id,link["link"],link["label"]])

    print(chunkid_text_dict)
    print(dependency_info)

    return dependency_info,chunkid_text_dict
```

```json

{0: '公園 から 、', 1: '望遠鏡 で', 2: '星 を', 3: '見 て い る', 4: '女の子 を', 5: '見 た'}
[[3, 1, 'implement'], [3, 2, 'object'], [4, 3, 'adjectivals'], [5, 0, 'source'], [5, 4, 'object']]
```

### graphvizを使ってみる

pythonライブラリであるgraphvizを利用して、係り受け関係をグラフ化する。

```python

from graphviz import Digraph
def graphviz_parse(jsonfile):
    dependencies,chunks = extract_dependency_info(jsonfile)

    # グラフの初期化
    G = Digraph(format='png')
    G.attr('node', shape="circle")

    # 係り受け情報よりノードとエッジを追加
    for dependency in dependencies:
        source = chunks[dependency[0]]
        G.edge(chunks[dependency[0]],chunks[dependency[1]],label=dependency[2])

    # グラフ描写
    G.render("graphs")
```

![graphs.png](https://qiita-image-store.s3.amazonaws.com/0/293276/700bbed3-0276-0371-ae27-b67e1664a477.png)

女の子が望遠鏡を道具として(`implement`)使っていることや、動作主が公園を動作の開始場所として(`source`)いることが、一目で理解することができる。

## 公式の可視化デモ

ちなみに公式HPにて、トークン間の依存関係を含めた可視化デモを公開している。

* [可視化デモ(公園から、望遠鏡で星を見ている女の子を見た)](https://api.ce-cotoha.com/demo?query=%E5%85%AC%E5%9C%92%E3%81%8B%E3%82%89%E3%80%81%E6%9C%9B%E9%81%A0%E9%8F%A1%E3%81%A7%E6%98%9F%E3%82%92%E8%A6%8B%E3%81%A6%E3%81%84%E3%82%8B%E5%A5%B3%E3%81%AE%E5%AD%90%E3%82%92%E8%A6%8B%E3%81%9F)


# 参考

下記を参考とさせていただきました
* [自然言語処理を簡単に扱えると噂のCOTOHA APIをPythonで使ってみた](https://qiita.com/gossy5454/items/83072418fb0c5f3e269f)
* [graphvizを使ってPython3で木構造を描く](https://qiita.com/shimo_t/items/b761973805f2cf0b2967)