"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

Pythonで日本語を単語単位に分割&品詞情報付与を行う(形態素解析)

f:id:arakan_no_boku:20210912183619p:plain

目次

日本語を単語に分割するのは簡単ではありません

日本語の文章を読み込んで、単語に分割する処理はよく必要になります。

単語をカウントするとかには絶対必要ですし。

でも「単語に分割する」は日本語ではとても困難です。

単語ごとに空白で区切られている李後と違い、日本語の文章には単語毎の区切りがなく、同じ文字でも前後の単語との関係で品詞が変わり、区切られる位置も変わります。

幸いなことに、今では、先人の研究によって、自分らでも使える「日本語を単語に分割してくれるツール」が存在します。

Pythonで使える主なツールは以下の2つです。

MeCab

f:id:arakan_no_boku:20180802144817j:plain

多分、一番有名で利用されているものです。

JavaC++python他様々な言語で使えます。

なんと、EXCELのVBAで使えるものを公開してくれている方とかも居ます。

Python3版もあります。

pypi.org

こちらもPIPで簡単にインストールできそうです。

ただ、自分が使い始めた当時にはなかったので、janomeのほうを使っていて、こちらは自分で試したことはありません。

Janome

100%、pythonで書かれています。

pypi.org

辞書はMeCabと同じものが標準で同梱されてます。

速度が遅いとか言われてますが、自分程度の使い方なら遅いと感じることはないです。

以降の説明では、Janomeを使って行っていきます。

とりあえず、janomeをインストールします。

pip install Janome

とりあえず単語分割と品詞分解をやってみる

janomeを使って、「私は田舎で育ちました」という文章を処理します。

from janome.tokenizer import Tokenizer

t = Tokenizer()
malist = t.tokenize("私は田舎で育ちました。")
for n in malist:
    print(n.surface + ":" + n.part_of_speech)

  

 結果はこんな感じです。

:名詞,代名詞,一般,*
:助詞,係助詞,*,*
田舎:名詞,一般,*,*
:助詞,格助詞,一般,*
育ち:動詞,自立,*,*
まし:助動詞,*,*,*
:助動詞,*,*,*
:記号,句点,*,*

単語に分割し、品詞情報が付与されています。

必要な単語だけを品詞で判断して抜き出す 

上記の結果には、何の処理に使うにせよ不要な情報が含まれているのがわかります。

代表的なのは、助詞とか記号ですかね。

単語の出現数をカウントするにしても名詞だけカウントしたいとか、普通にあります。

なので、今度は目指す品詞だけを処理するように改善してみます。

とりあえず、名詞と動詞と助動詞だけ残すパターンです。

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

#正規表現
pat = r'名詞|動詞|助動詞'
regex = re.compile(pat)

t = Tokenizer()
malist = t.tokenize("私は田舎で育ちました。")
for n in malist:
    if(regex.match(n.part_of_speech)):
        print(n.surface + ":" + n.part_of_speech)

 

こんな感じで実行すると、指定の品詞のみが抽出されます。

私:名詞,代名詞,一般,*
田舎:名詞,一般,*,*
育ち:動詞,自立,*,*
まし:助動詞,*,*,*
た:助動詞,*,*,**

同じ意味なのに異なる表現の言葉がある

日本語をコンピュータ処理するときにとても面倒くさいことが一つあります。

それは、同音異義語がたくさんあることです。

例えば、「私は田舎で育ちました。」の「私」ひとつとっても、「わたし」「ワタシ」「俺」「僕」「おいら」「自分」など・・バリエーションは沢山あります。

意味合い的には「私は田舎で育ちました」と「わたしはいなかでそだちました」と「僕はイナカで育ちました」は、まったく同じなんですが、文字コードで判定するとまったく違うものになります。

もし、「私」「わたし」「ワタシ」「僕」「俺」「自分」を、同一のものとして扱わないと具合が悪い場合なら、前処理で置き換えて、例えば「私」に無理やり統一してしまうなどが必要になるので、今後はその例をやります。

サンプルなので、「私は俺だし、僕でもあるし、自分でもあるのだよ。」という文章の、僕・俺・自分の3つを、全部「私」に置き換えるだけにします。

from janome.tokenizer import Tokenizer
import re

# 正規表現
pat = r'名詞|動詞|助動詞'
regex = re.compile(pat)

d = {"僕": "私", "俺": "私", "自分": "私"}
t = Tokenizer()
malist = t.tokenize("私は俺だし、僕でもあるし、自分でもあるのだよ。")
for n in malist:
    if(regex.match(n.part_of_speech)):
        if(n.surface in d):
            print(d[n.surface] + ":" + n.part_of_speech)
        else:
            print(n.surface + ":" + n.part_of_speech)

 

実行すると、すべて私に置き換えて出力されます。

私:名詞,代名詞,一般,*
私:名詞,代名詞,一般,*
だ:助動詞,*,*,*
私:名詞,代名詞,一般,*
ある:動詞,自立,*,*
私:名詞,一般,*,*
ある:動詞,自立,*,*
の:名詞,非自立,一般,*
だ:助動詞,*,*,*

実際には、同音異義語の辞書ファイルを用意するのが、とてつもなく大変です。

なので、まあ、雰囲気だけ。

おまけ

当たり前ではありますが、こういうテキスト処理をする場合、の文字コードを統一しておかないとうまくいきません。

ランダムにテキストファイルを集めると、あるファイルはSJIS、あるファイルはUTF8みたいなことがよくあるので、基本、UTF8にそろえておくことに注意します。

ではでは。