アイソモカ

知の遊牧民の開発記録

開発記録 200106 Mon (100本ノック #048)

新年快樂。

冬休みは帰省などでドタバタしていて、開発が全然進められませんでした(統計の本はちょっとだけ読んだ)。

そして、今日から仕事が始まったわけですけど、金曜まで頑張れるのか不安ですわあ……。

 

言語処理100本ノック #048

48. 名詞から根へのパスの抽出

文中のすべての名詞を含む文節に対し,その文節から構文木の根に至るパスを抽出せよ. ただし,構文木上のパスは以下の仕様を満たすものとする.

  • 各文節は(表層形の)形態素列で表現する
  • パスの開始文節から終了文節に至るまで,各文節の表現を"->"で連結する

「吾輩はここで始めて人間というものを見た」という文(neko.txt.cabochaの8文目)から,次のような出力が得られるはずである.  

吾輩は -> 見た

ここで -> 始めて -> 人間という -> ものを -> 見た

人間という -> ものを -> 見た

ものを -> 見た

 

調べたことメモ

Python:リストの中身を任意の区切り文字で出力する

区切り文字に \tを使うなら、各要素をこれで join する。

print('\t'.join([str(i) for i in list]))

参考 👉 Pythonでリストをタブ区切りで表示する - ブログ

VS Code:Run Code を止める

うっかり無限ループに入ってもーた。

出力のウィンドウ範囲内で右クリック

右クリック>Stop Code Runを選択するか、 ショートカットキーAlt+Ctrl+Mで止める。

参考 👉 VSCode "情報 code is already running" と出て止められない場合の対処。 - Qiita

 

完成

# knock048.py
# NLP Knock #048 by piijey
import function_filetochunklist

cabochafile = 'neko.txt.cabocha'

# 指定された品詞を含む場合に、係り先文節を返す
def getDst(chunk, poslist):
    for m in chunk.morphs:
        if m.pos in poslist and chunk.dst > 0:
            return chunk.dst
    return None

# 名詞を含む文節と係り先文節の表層形のリストを返す
def getNounRoot(sentence):
    if len(sentence) > 0:
        for chunk in sentence:
            ndst = getDst(chunk, ['名詞'])

            # 名詞以外・係り先文節なしなら終わり
            if ndst is None: 
                continue

            # 名詞をリストに入れる
            nr_list = [chunk.returnmorphs] 

            # 構文木の根に至るまでのループ
            # 係り先を探し、リストに追加していく
            while ndst is not None:
                nr_list.append(sentence[ndst].returnmorphs)
                if sentence[ndst].dst < 0:
                    ndst = None
                else:
                    ndst = sentence[ndst].dst

            # リストを返す
            yield nr_list

    else:
        return False

if __name__ == '__main__':
    sentences = list(function_filetochunklist.fileToChunkList(cabochafile))
    for i, s in enumerate(sentences):
        nroots_list = list(getNounRoot(s))

        if len(nroots_list) < 1:
            continue

        # リストの要素を区切り文字で join して出力
        for nr in nroots_list:
            print(' -> '.join([i for i in nr]))
        nroots_list = []

 

結果

$ python knock048.py >> knock048.txt (12行目まで)

吾輩は -> 猫である
名前は -> 無い
どこで -> 生れたか -> つかぬ
見当が -> つかぬ
何でも -> 薄暗い -> 所で -> 泣いて -> 記憶している
所で -> 泣いて -> 記憶している
ニャーニャー -> 泣いて -> 記憶している
いた事だけは -> 記憶している
吾輩は -> 見た
ここで -> 始めて -> 人間という -> ものを -> 見た
人間という -> ものを -> 見た
ものを -> 見た

 

おまけ:#044で使った有向グラフで可視化

knock048.py メインの後半部分を下のように書き換え、さきほどの出力を有向グラフ化してみる。

        dg = Digraph(format='png')
        for nr in nroots_list:
            for j in range(len(nr)-1):
                dg.edge(str(nr[j]), str(nr[j+1]))  # j -> j+1
        nroots_list = []
        dg.render('./knock48_data/'+str(i)+'.dot', view=True)

 ↓ 結果

「吾輩はここで始めて人間というものを見た」という文の 名詞を含む文節から構文木の根に至るパス

f:id:piijey:20200106222102p:plain
有向グラフ