この記事のすべてのコードはこのGoogle Colabから試すことができます。
SudachiPyの品詞
形態素解析後、形態素の品詞によって異なる処理をしたいということがよくあります。SudachiPyでは品詞情報はMorpheme
のpart_of_speech()
とpart_of_speech_id()
のメソッドでアクセスできます。
import sudachipy
dic = sudachipy.Dictionary()
tok = dic.create()
morphs = tok.tokenize("品詞によって違う処理をする必要があります")
for m in morphs:
print(m.surface(), m.part_of_speech(), sep="\t")
品詞 ('名詞', '普通名詞', '一般', '*', '*', '*')
に ('助詞', '格助詞', '*', '*', '*', '*')
よっ ('動詞', '一般', '*', '*', '五段-ラ行', '連用形-促音便')
て ('助詞', '接続助詞', '*', '*', '*', '*')
違う ('動詞', '一般', '*', '*', '五段-ワア行', '連体形-一般')
処理 ('名詞', '普通名詞', 'サ変可能', '*', '*', '*')
を ('助詞', '格助詞', '*', '*', '*', '*')
する ('動詞', '非自立可能', '*', '*', 'サ行変格', '連体形-一般')
必要 ('名詞', '普通名詞', '形状詞可能', '*', '*', '*')
が ('助詞', '格助詞', '*', '*', '*', '*')
あり ('動詞', '非自立可能', '*', '*', '五段-ラ行', '連用形-一般')
ます ('助動詞', '*', '*', '*', '助動詞-マス', '終止形-一般')
一番簡単なやりかたは直接品詞Tupleの要素の文字列で条件を作ることです。
for m in morphs:
print(m.surface(), m.part_of_speech()[0] == "名詞", sep="\t")
品詞 True
に False
よっ False
て False
違う False
処理 True
を False
する False
必要 True
が False
あり False
ます False
PosMatcher
SudachiPy 0.6.1以降では、品詞が定義された集合に含まれているかというチェックを簡単に行えるようにPosMatcherのAPIが追加されました。PosMatcherはDictionaryのpos_matcher
の関数を呼び出して作ります。引数の型によって2つの使い方があります。
関数から生成
一つ目の作り方は、pos_matcherの引数としてCallable(例えば、lambda関数)を渡すことです。品詞タグに対して真偽を返すような関数を渡すと、同様の動作をするPosMatcherが作られます。
m1 = dic.pos_matcher(lambda x: x[0] == "名詞")
for m in morphs:
print(m.surface(), m1(m), sep="\t") # PosMatcherを関数として使います
品詞 True
に False
よっ False
て False
違う False
処理 True
を False
する False
必要 True
が False
あり False
ます False
部分品詞リストから生成
二つ目の作り方は、引数として部分品詞のリストを渡すことです。部分品詞とは、品詞Tupleの一部を省略可能な書き方です。省略したい要素をNoneにすると、ワイルドカードとしてマッチします。
("名詞",) # 品詞の一層目が"名詞"のすべての品詞。Tupleのシンタックスとして、最後のカンマは必要。
(None, None, "サ変可能") # 品詞の2層目が"サ変可能"。
() # すべての品詞にマッチする。
部分品詞のPosMatcherの使い方の例は以下です。
m2 = dic.pos_matcher([("名詞",)])
for m in morphs:
print(m.surface(), m2(m), sep="\t")
品詞 True
に False
よっ False
て False
違う False
処理 True
を False
する False
必要 True
が False
あり False
ます False
集合の処理
一度作ったPosMatcherのオブジェクトを集合の処理で違うPosMatcherに変えることもできます。基本的な4つの処理を実装しています。Dictionary.pos_matcher
で同じPosMatcherを作ることも可能ですが、場合によって集合の処理での作り方のほうがわかりやすいケースもあります。この機能はSudachiPy 0.6.3以降です。
合併 (和集合)
nouns = dic.pos_matcher(lambda x: x[0] == "名詞")
verbs = dic.pos_matcher(lambda x: x[0] == "動詞")
verbs_or_nouns = nouns | verbs
for m in morphs:
print(m.surface(), verbs_or_nouns(m), sep="\t")
品詞 True
に False
よっ True
て False
違う True
処理 True
を False
する True
必要 True
が False
あり True
ます False
交叉 (積集合)
conj_form = dic.pos_matcher(lambda x: x[5] == "連用形-一般")
verbs_in_conj_form = verbs & conj_form
for m in morphs:
print(m.surface(), verbs_in_conj_form(m), sep="\t")
品詞 False
に False
よっ False
て False
違う False
処理 False
を False
する False
必要 False
が False
あり True
ます False
差・相対補 (差集合)
suru_possible = dic.pos_matcher(lambda x: x[2] == "サ変可能")
nouns_suru_not_possible = nouns - suru_possible
for m in morphs:
print(m.surface(), nouns_suru_not_possible(m), sep="\t")
品詞 True
に False
よっ False
て False
違う False
処理 False
を False
する False
必要 True
が False
あり False
ます False
補・絶対補 (補集合)
not_nouns = ~nouns
for m in morphs:
print(m.surface(), not_nouns(m), sep="\t")
品詞 False
に True
よっ True
て True
違う True
処理 False
を True
する True
必要 False
が True
あり True
ます True
PosMatcherの品詞
PosMatcherに入っている品詞とその数を参照することもできます。Pythonの普通のlen
関数で辞書の品詞セット中のマッチした品詞の数を調べることができます。
print(len(verbs_in_conj_form))
print(list(verbs_in_conj_form))
108
[('動詞', '一般', '*', '*', '五段-ラ行', '連用形-一般'), ('動詞', '一般', '*', '*', '下一段-マ行', '連用形-一般'), ...]
PosMatcherのiter
は必ず品詞をID順で返します。すべての品詞にマッチするPosMatcherは現在のすべての品詞をIDで参照可能な順番で返します。map((y, x) for (x, y) in enumerate(matcher))
で品詞→品詞IDのmap
を作ることができます。
速度
PosMatcher
のもう一つの目的は品詞分岐の高速化です。ロジックはRustで実装されて、品詞IDで高速に品詞のチェックをします。速度の比較は以下の3つのケースで行いました。コードはColabに載っています。
# Baseline: 品詞情報をつかわない
count = 0
for m in morphs:
count += 1
# Case 1: 品詞情報をそのまま使う
count = 0
for m in morphs:
pos = m.part_of_speech()
if pos[0] == "名詞" or pos[0] == "動詞":
count += 1
# Case 2: PosMatcherを使う
count = 0
for m in morphs:
if matcher(m):
count += 1
Baselineは2.5マイクロ秒程度、Case1は5.6マイクロ秒、Case2は4.1マイクロ秒でした。Baselineにくらべると、Case1は3.1マイクロ秒のオーバーヘッドがあり、Case2は1.6マイクロ秒のオーバーヘッドがあります。この簡単なテストではPosMatcherのオーバーヘッドは2分の1です。条件が複雑になればなるほど、この差が広がります。
終わりに
PosMatcherはSudachiPyでMorphemeをフィルタリングするための簡単なAPIです。直接品詞を参照するよりおすすめです。コードの簡略化と処理速度の向上のメリットがあります。
よいSudachi Lifeを。