MeCabとPythonで品詞を選びつつ分かち書きをしたよ
どうも、この前Juman++で形態素解析をするのをおすすめしちゃいましたが、自分は現在でもMeCabを使用しています。というのもJuman++では、なんとなく語彙数が少なすぎる感じがするからです(ちゃんとした検証は行っていないので、これはオフレコということでおねがいします)。というわけで、今回はMeCabをPythonから実行することで分かち書きを行うプログラムを作成したので、ここに掲載しておきます。今回は単純な分かち書きだけではなく、特定の品詞の単語を除外したり、自分で定義した書き換えルールに則って特定の品詞の語を別の語に置き換えるという処理を持たせてみました。
今回も例にならいまして、すでにPythonからMeCabを操作できる準備はできているものとして紹介します。それぞれのインストールは別サイトさんを参照するか、本サイトの作業備忘録を参考にしていてください。
OS : Ubuntu 16.04 LTS
Python : 3.5.2
1. 書いたプログラム
今回書いたプログラムは以下のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
#coding:utf-8 import argparse import MeCab # 実行引数の受け取り parser = argparse.ArgumentParser(description="MeCabで分かち書きを行うためのスクリプトです") parser.add_argument('input', type=str, help="入力ファイルへのPath(必須)") parser.add_argument('-o', '--output', type=str, default="./out.txt", help="出力ファイルへのPath(Default:out.txt)") parser.add_argument('-t', '--target', type=str, nargs='+', default=[], help="出力する品詞(Default:ALL)") parser.add_argument('-e', '--exclusion', type=str, nargs='+', default=["記号","BOS/EOS"], help="解析時に除外する品詞(Default:記号,BOS/EOS)") parser.add_argument('-e1','--exclusion1', type=str, nargs='+', default=[], help="解析時に除外する品詞細分類1(Default:None)") parser.add_argument('-e2','--exclusion2', type=str, nargs='+', default=[], help="解析時に除外する品詞細分類2(Default:None)") parser.add_argument('-e3','--exclusion3', type=str, nargs='+', default=[], help="解析時に除外する品詞細分類3(Default:None)") parser.add_argument('-s', '--splitchar', type=str, default=' ', help="分かち書きを行う際に各形態素の間に挿入される文字です(Default:Space)") parser.add_argument('-m', '--outputmode', type=str, default='genkei', choices=['hyousou', 'genkei', 'yomi'], help="出力する形態素の形式を設定します 表層形:hyousou,原形:genkei,読み:yomi(Default:genkei)") args = parser.parse_args() # 書き換え規則の定義 # 基本的には単純なディクショナリ型(キー:書き換える品詞, 値:書き換え後の単語) # 書き換える品詞は入れ子状に記述可能(品詞→品詞細分類1→品詞細分類2→品詞細分類3の順で入れ子にしていく) # Default 1) "名詞"でかつ品詞細分類1が"数"ならば"[数値]"に単語を置き換えて出力 # 2) "名詞"でかつ品詞細分類1が"固有名詞"でかつ品詞細分類2が"組織"ならば"[固有名詞_組織]"に単語を置き換えて出力 replace_rule = { '名詞': { '数': "[数値]", '固有名詞': { '組織': "[固有名詞_組織]" } } } # 入力ファイルと出力ファイルの準備 in_file = open(args.input) out_file = open(args.output, 'w') # MeCabのインスタンス作成 mecab = MeCab.Tagger('-Ochasen') mecab.parseToNode('') # MeCab上の不具合で一度空で解析すると解消される # 一行毎に解析開始 for line in in_file: text = '' node = mecab.parseToNode(line) # 一単語毎に解析 while node: # 形態素のノードから形態素情報を取得 # 得られる形態素情報は基本的に以下の順番のリストになっている # [品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音] word_surface = node.surface word_features = node.feature.split(',') # 単語の置き換え規則の確認及び実行 local_replace_rule = replace_rule for x in range(3): if word_features[x] in local_replace_rule: if isinstance(local_replace_rule[word_features[x]], dict): local_replace_rule = local_replace_rule[word_features[x]] elif isinstance(local_replace_rule[word_features[x]], str): word_surface = local_replace_rule[word_features[x]] word_features = word_features[:5] + [word_surface, word_surface, word_surface] break # 解析対象の品詞か確認 if (not len(args.target) or word_features[0] in args.target) and \ word_features[0] not in args.exclusion and \ word_features[1] not in args.exclusion1 and \ word_features[2] not in args.exclusion2 and \ word_features[3] not in args.exclusion3: # 指定された形式で出力に追加 if args.outputmode == 'hyousou': word = word_surface elif args.outputmode == 'genkei': word = word_features[6] elif args.outputmode == 'yomi': word = word_features[7] # 未定義(辞書内で"*"と表記される)の場合は出力しない if word is not '*': text += word + args.splitchar node = node.next # 解析した行に形態素があれば出力ファイルに記述 if text is not '': out_file.write(text + "\n") in_file.close() out_file.close() |
今回のプログラムでは、MeCabを使った単純な分かち書きだけでなく、特定の品詞を除外したり、自分で定義した書き換えルールに該当する単語を別の単語に置き換えるという機能をもたせています。その分動作は遅くなり、使い方も複雑になってしまいましたが、後悔はしていません。
2. 使い方
では、次に一応使い方を紹介しておきます(主に将来の自分用になると思いますが…)。紹介の便宜上上記のプログラムは”mian.py”というファイルに記述されていて、分かち書きをしたいテキストファイルは”input.txt”という名前ということにします。まずは基本的な使い方ですが、以下のコマンドで実行することができます。
1 |
$ python3 main.py input.txt |
この実行により、品詞が”記号”と”EOS/BOS”は無視しつつ、他の品詞の語は原形で出力されるという結果が得られます。さらに、入力テキスト内で品詞が”名詞”でかつ品詞細分類1が”数”のものを”[数値]”という単語に置き換え、”名詞”でかつ品詞細分類1が”固有名詞”でかつ品詞細分類2が”組織”のものを”[固有名詞_組織]”という単語に置き換えるという処理を行っています。
また、出力先を変更したい場合は、以下のコマンドのように”-o”というオプションを持たせれば大丈夫です。 ちなみに、デフォルトでは、out.txtというファイルに出力されます。
1 |
$ python3 main.py input.txt -o out_text.txt |
おまけ機能として、出力される形態素の形式を選択できるようにしておきました。選択できる形式は、表層形と原形そして読みの三種類です。設定方法は以下の例のように実行時に”-m”というオプションをもたせるだけです。
1 2 3 |
$ python3 main.py input.txt -m hyousou $ python3 main.py input.txt -m genkei $ python3 main.py input.txt -m yomi |
これらは、ご自身のタスクに合わせて選択してください。しかし、この機能なのですが、MeCabで使用している辞書の書き方によっては正しく動作しない可能性があるので注意です。心配なのは「読み」を出力するときなのですが、ちゃんと読みが定義されていない辞書では、エラーが出ます。これに関してはちゃんとした対策はしていないので、みなさんが注意してくれると助かります。
i. 対象にする品詞を選ぶ
上記のコマンドに実行引数を持たせることによって対象にする品詞を選ぶことができます。選ぶための引数は”-t”と”-e”で指定することができます。”-t”では、対象とする品詞を選べます(何も指定しなければすべての品詞が対象)。また、”-e”では対象外にする品詞を選べます(デフォルトでは”記号”と”EOS/BOS”を対象外にしている)。つまり、このどちらも引数に持たせない場合だと、(なかなか気持ち悪い仕様ではありますが、)記号とEOS/BOSの品詞をもつ形態素は出力から除外されるということです。以下にいくつか実行例を載せておきます。
1 2 3 4 5 6 |
$# 対象品詞を"名詞"と"動詞"に絞る場合 $ python3 main.py input.txt -t 名詞 動詞 $# 対象外品詞に助詞も入れる場合 $ python3 main.py input.txt -e 記号 EOS/BOS 助詞 $# "未知語"と"特殊"も解析対象にする場合 $ python3 main.py input.txt -e "" |
対象外にする品詞は、「品詞細分類1,2」を限定することでより細かく設定することができます。設定方法は、それぞれ”-e1″, “-e2″というオプションでもって品詞を宣言すれば大丈夫です。
ii. 書き換え規則の定義
上記で示しました、単語の書き換え規則なのですが、文字で説明するとかなりややこしいので、解析結果例を下記に示します。
1 2 3 4 5 6 7 |
*** 入力テキスト 吾輩は123の猫である hjkdfssdfkjのじゃがいも *** 出力結果 我輩 は [数値] の 猫 だ ある [固有名詞_組織] の じゃがいも |
このような置き換え処理は、プログラム内(24行目辺りのreplace_ruleの宣言箇所)で定義します。定義の仕方が多少ややこしくなってしまいましたが、大きな区切りとしての「品詞」だけでなく「品詞細分類1,2」を考慮するためこのような形になりました。基本的には、入れ子状に定義してくだされば大丈夫です。もし、「品詞細分類2」についてのルールを定義したい場合は、「{品詞名:{品詞細分類1名:{品詞細分類2名:”[書き換え後の語]”}}}」という書き方になります。何かもっとわかりやすい書き方があったら教えてください。
ちなみに、なぜ”固有名詞_組織”の書き換えを行っているかを簡単に説明すると、至極単純でこれは未知語である可能性が高いからです。以上。
おわりに
というわけで、分かち書きのプログラムでした。今回は、自分が使うように書いたので、かなり自己満足的なプログラムになってしまいました。正直、ただ分かち書きを行いたいなら、MeCabだけでできるのでコマンド1行で終わってしまいます。つまり今回挙げた例は機能を持たせればもたせるほど使い方がややこしくなるという、ある意味でいいプログラムだと思います。………何いってんだか分からなくなったところで今回はここまで。
ディスカッション
ピンバック & トラックバック一覧
[…] この実装も、こちらのコードを基本的には拝借しました。 ただ、いくつか改善点があって、基本は以下の2点です。 […]