2018年07月05日

言語処理100本ノックでPython入門 #57 - pydot_ngで係り受け解析を可視化する

  

今日は、言語処理100本ノック 2015の第6章・問題57です。

■ 問題

57. 係り受け解析
Stanford Core NLPの係り受け解析の結果(collapsed-dependencies)を有向グラフとして可視化せよ.可視化には,係り受け木をDOT言語に変換し,Graphvizを用いるとよい.また,Pythonから有向グラフを直接的に可視化するには,pydotを使うとよい.


■ どうやって解くか考える

Stanford Core NLPの係り受け解析の結果のXMLファイルを見てみると、各sentenceタグの中に以下のような記述があります。
 <dependencies type="collapsed-dependencies">
  <dep type="root">
    <governor idx="0">ROOT</governor>
    <dependent idx="2">language</dependent>
  </dep>
  <dep type="amod">
    <governor idx="2">language</governor>
    <dependent idx="1">Natural</dependent>
  </dep>
  <dep type="dep">
    <governor idx="2">language</governor>
    <dependent idx="3">processing</dependent>
  </dep>
  <dep type="punct">
    <governor idx="2">language</governor>
    <dependent idx="4">.</dependent>
  </dep>
</dependencies>
governorが親ノード、dependentが子ノードを示しているようです。 

これを読み込み、Graphvizを使って有向グラフを可視化します。

dependencies要素の type属性の値は、"basic-dependencies", "enhanced-dependencies"など他の値もありますが、ここでは、問題にあるように "collapsed-dependencies" だけを対象としました。

可視化には、No.44でもやったように、pydot_ngを使います。

なお、ドットとカンマは可視化から除外しています。



■ Pythonのコード
from xml.etree import ElementTree
import os
import pydot_ng as pydot

class CollapsedDependencies:
    def __init__(self, filepath):
        xdoc = ElementTree.parse(filepath)
        root = xdoc.getroot()
        self.sentences = root.find('document/sentences')
        self.coreference = root.find('document/coreference')

    def enumCoreference(self):
        for e in self.coreference:
            yield e

    @staticmethod
    def toDot(deps):
        edges = []
        for dep in deps:
            governor = dep.find('governor')
            dependent = dep.find('dependent')
            if dependent.text != '.' and dependent.text != ',':
                edges.append((governor.text, dependent.text))
        return edges

    def getDependence(self, sentenceId):
        strid = str(sentenceId)
        sentences = self.sentences.find("sentence[@id='" + strid + "']")
        deps = sentences.find('dependencies[@type="collapsed-dependencies"]')
        return self.toDot(deps)

    def enumDependencies(self):
        dependencies = self.sentences.findall('sentence/dependencies[@type="collapsed-dependencies"]')
        for deps in dependencies:
            yield self.toDot(deps)

    @staticmethod
    def toGraph(dot, filepath):
        os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin/'

        graph = pydot.Dot(graph_type='digraph')
        graph.set_node_defaults(fontname='Meiryo UI', fontsize='10')

        for s, t in dot:
            graph.add_edge(pydot.Edge(s, t))
        graph.write_png(filepath)

def main():
    cd = CollapsedDependencies('chap06/nlp.txt.xml')
    # sentenceId = 1
    # for dot in cd.enumDependencies():
    #     cd.toGraph(dot, "g57_{}.png".format(sentenceId))
    #     sentenceId += 1

    # ここでは、sentenceIdを指定して1文だけを処理するコードを実行
    sentenceId = 7
    dot = cd.getDependence(sentenceId)
    cd.toGraph(dot, "ag57_{}.png".format(sentenceId))

if __name__ == '__main__':
    main()

■ 結果

sentenceId = 7を処理した時の結果です。


ag57_7