形態素解析で代名詞+助詞+名詞を文章から抜き出す【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.Analyzer
とjanome.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
変数には「坊っちゃん」の文章を保存しておきます。
夏目漱石の「坊っちゃん」は青空文庫で無料で公開されています。
以上です。