CaboChaの係り受けをpydotで描いてみる

[CaboCha](https://taku910.github.io/cabocha/) でパースした結果を `FORMAT_XML` するとこんな感じになるので、

```xml
<sentence>
 <chunk id="0" link="1" rel="D" score="1.990242" head="0" func="1">
  <tok id="0" feature="名詞,固有名詞,一般,*,*,*,金融商品取引法,キンユウショウヒントリヒキホウ,キンユーショーヒントリヒキホー">金融商品取引法</tok>
  <tok id="1" feature="助詞,格助詞,一般,*,*,*,に,ニ,ニ">に</tok>
 </chunk>
 <chunk id="1" link="7" rel="D" score="0.627582" head="2" func="3">
  <tok id="2" feature="動詞,自立,*,*,五段・カ行イ音便,連用タ接続,基づく,モトヅイ,モトズイ">基づい</tok>
  <tok id="3" feature="助詞,接続助詞,*,*,*,*,て,テ,テ">て</tok>
  <tok id="4" feature="記号,読点,*,*,*,*,、,、,、">、</tok>
 </chunk>
 <chunk id="2" link="3" rel="D" score="0.900482" head="5" func="6">
  <tok id="5" feature="名詞,固有名詞,一般,*,*,*,事業年度,ジギョウネンド,ジギョーネンド">事業年度</tok>
  <tok id="6" feature="助詞,並立助詞,*,*,*,*,や,ヤ,ヤ">や</tok>
 </chunk>
 <chunk id="3" link="4" rel="D" score="2.602002" head="7" func="8">
  <tok id="7" feature="名詞,一般,*,*,*,*,四半期,シハンキ,シハンキ">四半期</tok>
  <tok id="8" feature="助詞,連体化,*,*,*,*,の,ノ,ノ">の</tok>
 </chunk>
 <chunk id="4" link="7" rel="D" score="0.888829" head="11" func="12">
  <tok id="9" feature="名詞,サ変接続,*,*,*,*,終了,シュウリョウ,シューリョー">終了</tok>
  <tok id="10" feature="名詞,固有名詞,一般,*,*,*,3か月,サンカゲツ,サンカゲツ">3カ月</tok>
  <tok id="11" feature="名詞,非自立,副詞可能,*,*,*,以内,イナイ,イナイ">以内</tok>
  <tok id="12" feature="助詞,格助詞,一般,*,*,*,に,ニ,ニ">に</tok>
 </chunk>
 <chunk id="5" link="7" rel="D" score="1.938762" head="13" func="14">
  <tok id="13" feature="名詞,固有名詞,一般,*,*,*,金融庁長官,キンユウチョウチョウカン,キンユーチョーチョーカン">金融庁長官</tok>
  <tok id="14" feature="助詞,格助詞,一般,*,*,*,に,ニ,ニ">に</tok>
 </chunk>
 <chunk id="6" link="7" rel="D" score="2.378815" head="15" func="16">
  <tok id="15" feature="名詞,サ変接続,*,*,*,*,提出,テイシュツ,テイシュツ">提出</tok>
  <tok id="16" feature="助詞,格助詞,一般,*,*,*,が,ガ,ガ">が</tok>
 </chunk>
 <chunk id="7" link="8" rel="D" score="2.697188" head="17" func="20">
  <tok id="17" feature="動詞,自立,*,*,一段,未然形,義務づける,ギムヅケ,ギムズケ">義務づけ</tok>
  <tok id="18" feature="動詞,接尾,*,*,一段,連用形,られる,ラレ,ラレ">られ</tok>
  <tok id="19" feature="助詞,接続助詞,*,*,*,*,て,テ,テ">て</tok>
  <tok id="20" feature="動詞,非自立,*,*,一段,基本形,いる,イル,イル">いる</tok>
 </chunk>
 <chunk id="8" link="12" rel="D" score="-1.536080" head="21" func="22">
  <tok id="21" feature="名詞,一般,*,*,*,*,書類,ショルイ,ショルイ">書類</tok>
  <tok id="22" feature="助詞,格助詞,一般,*,*,*,で,デ,デ">で</tok>
  <tok id="23" feature="記号,読点,*,*,*,*,、,、,、">、</tok>
 </chunk>
 <chunk id="9" link="10" rel="D" score="1.541807" head="24" func="25">
  <tok id="24" feature="名詞,一般,*,*,*,*,事業,ジギョウ,ジギョー">事業</tok>
  <tok id="25" feature="助詞,連体化,*,*,*,*,の,ノ,ノ">の</tok>
 </chunk>
 <chunk id="10" link="11" rel="D" score="2.508582" head="31" func="33">
  <tok id="26" feature="名詞,一般,*,*,*,*,状況,ジョウキョウ,ジョーキョー">状況</tok>
  <tok id="27" feature="記号,一般,*,*,*,*,・,・,・">・</tok>
  <tok id="28" feature="名詞,一般,*,*,*,*,財務,ザイム,ザイム">財務</tok>
  <tok id="29" feature="名詞,一般,*,*,*,*,状態,ジョウタイ,ジョータイ">状態</tok>
  <tok id="30" feature="記号,一般,*,*,*,*,・,・,・">・</tok>
  <tok id="31" feature="名詞,固有名詞,一般,*,*,*,経営成績,ケイエイセイセキ,ケイエイセイセキ">経営成績</tok>
  <tok id="32" feature="助詞,副助詞,*,*,*,*,など,ナド,ナド">など</tok>
  <tok id="33" feature="助詞,連体化,*,*,*,*,の,ノ,ノ">の</tok>
 </chunk>
 <chunk id="11" link="12" rel="D" score="-1.536080" head="34" func="35">
  <tok id="34" feature="名詞,一般,*,*,*,*,情報,ジョウホウ,ジョーホー">情報</tok>
  <tok id="35" feature="助詞,格助詞,一般,*,*,*,を,ヲ,ヲ">を</tok>
 </chunk>
 <chunk id="12" link="-1" rel="D" score="0.000000" head="37" func="40">
  <tok id="36" feature="名詞,サ変接続,*,*,*,*,掲載,ケイサイ,ケイサイ">掲載</tok>
  <tok id="37" feature="動詞,自立,*,*,サ変・スル,連用形,する,シ,シ">し</tok>
  <tok id="38" feature="助詞,接続助詞,*,*,*,*,て,テ,テ">て</tok>
  <tok id="39" feature="動詞,非自立,*,*,一段,連用形,いる,イ,イ">い</tok>
  <tok id="40" feature="助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス">ます</tok>
  <tok id="41" feature="記号,句点,*,*,*,*,。,。,。">。</tok>
 </chunk>
</sentence>
```

pydotでグラフ描いてみた話。

![a.png](https://qiita-image-store.s3.amazonaws.com/0/41925/1f7e1dea-4adf-84a6-d667-8e20c09abce9.png)


# コード

無駄に指定したキーワードを赤くする機能も入れてみたり。

```python
import CaboCha
import lxml.etree
import pydot

def xml_to_chunks(xml_txt):
    root = lxml.etree.fromstring(xml_txt)
    assert root.tag == "sentence"

    ret = {}
    for chunk in root.getchildren():
        assert chunk.tag == "chunk"
        sentence = "".join([ token.text for token in chunk.getchildren() ])
        chunk_id = chunk.attrib["id"]
        ret[chunk_id] = (sentence, chunk)

    return ret

def create_pydot_node(label, elem, kwds):
    node_id = "%s_%s" % (elem.tag, elem.attrib["id"])
    for kwd in kwds:
        if kwd in label:
            fontcolor = "#FF0000"
            break
    else:
        fontcolor = "#000000"

    return pydot.Node(node_id, label=label, fontcolor=fontcolor)

def sentence_to_graph(parser, text, kwds):
    tree =  parser.parse(text)
    xml = tree.toString(CaboCha.FORMAT_XML)

    chunks = xml_to_chunks(xml)
    graph = pydot.Dot(graph_type='digraph')

    for sentence, elem in chunks.values():
        link = elem.attrib["link"]
        if link == "-1":
            continue

        link_sentence, link_elem = chunks[link]

        node1 = create_pydot_node(sentence, elem, kwds)
        node2 = create_pydot_node(link_sentence, link_elem, kwds)

        graph.add_node(node1)
        graph.add_node(node2)
        graph.add_edge(pydot.Edge(node1, node2))

    return graph


if __name__ == '__main__':
    parser = CaboCha.Parser("-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd")

    text = "金融商品取引法に基づいて、事業年度や四半期の終了3カ月以内に金融庁長官に提出が義務づけられている書類で、事業の状況・財務状態・経営成績などの情報を掲載しています。"
    keywords = ["事業"]
    g = sentence_to_graph(parser, text, keywords)
    g.write_png("a.png")
```