ユーニックス総合研究所

  • home
  • archives
  • python-janome-ppn

形態素解析で代名詞+助詞+名詞を文章から抜き出す【Python, 自然言語処理, Janome】

  • 作成日: 2020-10-10
  • 更新日: 2023-12-24
  • カテゴリ: Python

Pythonで形態素解析

人間が発する言語を「自然言語」と言い、それの解析を「自然言語処理」といいます。
さらに日本語の文章を単語ごとに分割することを「形態素解析」と言い、これを行うライブラリは「MeCab」などが有名です。

Pythonには「Janome」という形態素解析ライブラリがあり、こちらは手軽に導入することが可能です。

Janomeを使い、夏目漱石の「坊ちゃん」の文章を解析して、「代名詞 + 助詞 + 名詞」の組み合わせを文章から抜き出す処理を書きたいと思います。
代名詞と言うのは「俺」「お前」「それ」などで、助詞は「て」「に」「を」「は」、名詞は「リンゴ」「車」などの単語のことです。

Janomeのインストール

pipで形態素解析ライブラリのJanomeをインストールします。

$ pip install janome  
$ pip freeze  
Janome==0.4.0  

Janomeの使い方

Janomeで形態素解析を行いますが、最初にjanome.tokenizer.Tokenizerをインポートします。

from janome.tokenizer import Tokenizer  

Tokenizerは文章を形態素解析して、単語ごとに分割し、ジェネレーターを返すクラスです。
Tokenizer.tokenize()メソッドに文章を渡して実行することで解析を行えます。

from janome.tokenizer import Tokenizer  

t = Tokenizer()  
toks = t.tokenize('ここに日本語の文章')  
print(type(toks))  

Tokenizer.tokenize()の戻り値はgeneratorです。

<class 'generator'>  

これをfor文などで回すと単語の情報を含んだトークンを取り出せます。

for tok in toks:  
    print(tok)  
ここ    名詞,代名詞,一般,*,*,*,ここ,ココ,ココ  
に      助詞,格助詞,一般,*,*,*,に,ニ,ニ  
日本語  名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ  
の      助詞,連体化,*,*,*,*,の,ノ,ノ  
文章    名詞,一般,*,*,*,*,文章,ブンショウ,ブンショー  

トークンの情報

Tokenizer.tokenize()で生成されるトークン列のトークンにはさまざまな属性がありますが、今回使用する属性は↓の通りです。

  • surface
  • part_of_speech

Token.surfaceは「表層形」と呼ばれる単語の元の文章のままの文字列のことです。
このToken.surfaceを参照することで元の文章における単語の表現を取得できます。

from janome.tokenizer import Tokenizer  

t = Tokenizer()  
toks = list(t.tokenize('ここに日本語の文章'))  
print(toks[0].surface)  # 最初の単語のsurfaceを参照する  
ここ  

Token.part_of_speechはカンマ区切りで並べられた品詞の列です。
単語の品詞を判定したい場合はこのToken.part_of_speechを参照します。
今回の例で言えば「代名詞」と「助詞」、「名詞」を判定したいわけなので、このToken.part_of_speechにそれらの文字列が含まれているかどうかチェックします。

from janome.tokenizer import Tokenizer  

t = Tokenizer()  
toks = t.tokenize('ここに日本語の文章')  

for tok in toks:  
    print(tok.part_of_speech)  # 品詞を出力  
名詞,代名詞,一般,*  
助詞,格助詞,一般,*  
名詞,一般,*,*  
助詞,連体化,*,*  
名詞,一般,*,*  

ちなみにjanome.analyzer.Analyzerjanome.tokenfilterを使えば特定の品詞のトークンのみを文章から抽出することが出来ます。
しかし、今回は文脈上の「代名詞 + 助詞 + 名詞」の組み合わせを判定したいので、ここでは使用方法などは割愛します。

「代名詞 + 助詞 + 名詞」の組み合わせを抽出する

Janomeで文章を形態素解析してトークン列に分割したら、あとは自前のアナライズ関数を使って「代名詞 + 助詞 + 名詞」の組み合わせを抽出します。

このアナライズ関数は「状態遷移」という技術を使います。
状態を持つ変数mを定義し、解析の過程でこの変数mを変化させます。
変数mの値に応じて解析方法を柔軟に変更し、目的のトークン列があるかどうか判定します。
トークン列を抜き出したらyield文で結果を生成し、呼び出し元で参照できるようにします。

def analyze(toks):  
    """  
    「代名詞 + 助詞 + 名詞」の組み合わせのトークン列を抽出する  
    """  
    m = 0  
    buf = []  

    for tok in toks:  
        p = tok.part_of_speech.split(',')  
        if m == 0:  
            if '代名詞' in p:  
                m = 10  
                buf.append(tok)  
        elif m == 10:  # found 代名詞  
            if '助詞' in p:  
                m = 20  
                buf.append(tok)  
            elif '代名詞' in p:  
                buf = [tok]  
            else:  
                buf = []  
                m = 0  
        elif m == 20:  # found 助詞  
            if '名詞' in p:  
                buf.append(tok)  
                yield buf  
                buf = []  
                m = 0  
            else:  
                buf = []  
                m = 0  

ちなみに状態遷移を使わなくても書けます。
↓の例は状態遷移を使わずに単純に現在のトークンの2つ先まで先読みする方法です。
こちらのほうが解法としてはシンプルですね。

def analyze(toks):  
    """  
    「代名詞 + 助詞 + 名詞」の組み合わせのトークン列を抽出する  
    """  
    toks = list(toks)  
    i = 0  

    while i < len(toks) - 2:  
        t1 = toks[i]  
        t2 = toks[i + 1]  
        t3 = toks[i + 2]  
        i += 1  

        if '代名詞' in t1.part_of_speech.split(',') and \  
           '助詞' in t2.part_of_speech.split(',') and \  
           '名詞' in t3.part_of_speech.split(','):  
           yield [t1, t2, t3]  

🦝 < 状態遷移に頼りすぎな私

コード全文は↓になります。

# coding: utf-8  
from janome.tokenizer import Tokenizer  


def dump(toks):  
    for tok in toks:  
        print(tok)  


def analyze(toks):  
    """  
    「代名詞 + 助詞 + 名詞」の組み合わせのトークン列を抽出する  
    """  
    toks = list(toks)  
    i = 0  

    while i < len(toks) - 2:  
        t1 = toks[i]  
        t2 = toks[i + 1]  
        t3 = toks[i + 2]  
        i += 1  

        if '代名詞' in t1.part_of_speech.split(',') and \  
           '助詞' in t2.part_of_speech.split(',') and \  
           '名詞' in t3.part_of_speech.split(','):  
           yield [t1, t2, t3]  


def main():  
    # 「坊っちゃん」の文章  
    content = '''  
親譲おやゆずりの無鉄砲むてっぽうで...(中略)  
'''  

    t = Tokenizer()  
    toks = t.tokenize(content)  
    for toks in analyze(toks):  
        s = ''.join([tok.surface for tok in toks])  
        print(s)  


if __name__ == '__main__':  
    main()  

↑のコード中の夏目漱石の「坊ちゃん」の文章を解析すると↓のような結果になります。

君の指  
これは命  
こっちの胸  
おれの袷  
おれの二の腕  
おれの袷  

*> おれの二の腕

淑女の皆さんには刺激の強いワードが出てきてしまいました。

↑のコードのcontent変数には「坊っちゃん」の文章を保存しておきます。
夏目漱石の「坊っちゃん」は青空文庫で無料で公開されています。

以上です。