【놀이】신카리온의 톤 데모 메일을 구문 분석

신칸센 변형 로보 신카리온 라는 애니메이션으로 인상적이었던 이하와 같은 메일의 일문이 있었습니다.


말했는지 말하지 않았는지 어느 쪽인지 제대로 기억하고 있지 않지만, 확실히 그동안 손으로 감아 파티를 했을 때에 조금 말한 생각이 들지 않아도 없어도 아마 말했잖아, 라고 여기까지 생각 했지만, 뭐라고 말하는 듯이 말하고 있어도 거기까지 문제 없지요, 라고 생각하게 된 나름입니다.

이 메일은 애니메이션으로 1 초 정도밖에 나오지 않고 제작 스탭의 장난기가 가득한 문장입니다.
이번에는 그 문장을 Cabocha를 사용해 구문 분석해 pydot로 표시했습니다.
하고 있는 것은 기사 「타키자와 카렌의 이해할 수 없는 문장을 언어 해석해 보았다.」 의 2번 달입니다.

구문 분석 결과



긴 문장만 있어 큰・・・ 생각보다 잘 해석할 수 있습니다. 보면 볼수록 끔찍한 글입니다.


출처



파이썬으로 쓰고 있습니다. 언어 처리 100개 노크 사이에 약간의 숨결으로 만들었습니다. 그 프로그램의 유용하기 때문에 조금 쓸데없는 부분이 있습니다. 기사 「아마추어의 언어 처리 100개 노크:44」 를 참고로 하겠습니다.
import re, CaboCha, pydot

class Morph:
    def __init__(self, surface, pos):
        self.surface = surface # 表層形(surface)
        self.pos     = pos     # 品詞(pos)

class Chunk:
    def __init__(self):
        self.morphs = []
        self.srcs   = []   # 係り元文節インデックス番号のリスト
        self.dst    = -1   # 係り先文節インデックス番号(初期値:-1, 係り先がない場合は-1のまま)

    def output_surface(self):
        surface = ''
        for morph in self.morphs:

            # 記号を除外
            if morph.pos != '記号':
                surface += morph.surface
        return surface

def parse_lines(tree_list):
    chunks = dict()     # idxをkeyにChunkを格納

    for line in tree_list:
        if line[:3] == 'EOS':

            # Chunkのリストを返す
            if len(chunks) > 0:

                # chunksをkeyでソートし、valueのみ取り出し
                sorted_tuple = sorted(chunks.items(), key=lambda x: x[0])
                return list(zip(*sorted_tuple))[1]  #[1]がリストのvalue部分
                chunks.clear()

            else:
                return []

        # 先頭が*の行は係り受け解析結果なので、Chunkを作成
        elif line[0] == '*':

            # Chunkのインデックス番号と係り先のインデックス番号取得
            cols = re.split('\s|D', line)
            idx = int(cols[1]) # Chunkのインデックス番号
            dst = int(cols[2]) # 係り先文節インデックス番号

            # Chunkを生成(なければ)し、係り先のインデックス番号セット
            if idx not in chunks:
                chunks[idx] = Chunk()
            chunks[idx].dst = dst

            # 係り先のChunkを生成(なければ)し、係り元インデックス番号追加
            if dst != -1:
                if dst not in chunks:
                    chunks[dst] = Chunk()
                chunks[dst].srcs.append(idx) # 係り元は複数あるのでappend

        else:

            #タブとカンマで分割
            cols = re.split('\t|,', line)

            chunks[idx].morphs.append(Morph(
                    cols[0],    # 表層形(surface)
                    cols[1]     # 品詞(pos)
                ))

# 構文解析
parser = CaboCha.Parser()
tree = parser.parse('言ってあったか言ってなかったかどっちだったかちゃんと覚えていないけど、確かこの間手巻きパーティをやった時にちょこっと言った気がしなくもなきにしもあらずで多分言ったんじゃないかな、とココまで考えてみたけど、まあ言ってようが言っていまいがそこまで問題ないよね、と思うに至った次第です。')
#tree = parser.parse('今日の天気は晴れでしたが、明日の天気は悪くなるそうです。')

# Stringのlistに分解
tree_string = tree.toString(CaboCha.FORMAT_LATTICE)
tree_list = tree_string.splitlines()

# 係り受けを整理
chunks = parse_lines(tree_list)

# 係り先があるものをpydotに渡す形式に変更
edges = []
for i, chunk in enumerate(chunks):
    if chunk.dst != -1:

        # 記号を除いた表層形をチェック、空なら除外
        src = chunk.output_surface()
        dst = chunks[chunk.dst].output_surface()
        if src != '' and dst != '':
            edges.append(((i, src), (chunk.dst, dst)))

# pydotで有向グラフとして画像保存
 if len(edges) > 0:
    graph = pydot.graph_from_edges(edges, directed=True)
    graph.write_png('result.png')

좋은 웹페이지 즐겨찾기