やること
Colab環境でMeCabを使う3行 の補足記事です。
Colab環境でMeCabの出力結果を操る際、MeCab.Tagger()で要素を一旦取得してそこから読みカナなどの結果を抽出するとうまくいきます。
これを5行で実行してみます。
内包表記にすると1行で綺麗にまとまるのですが、内包表記は初心者にはフクザツすぎるため、その説明を超丁寧にやってみます。
ちなみにこの内包表記は前の記事のコメント欄にて教えていただいたものです。
環境
- Colaboratory環境
- ブラウザはChrome推奨
5行で実行する場合の例
! pip install -U mecab-python3 unidic-lite
import MeCab
tagger = MeCab.Tagger()
s = tagger.parse("糠に釘")
print("".join(w.split()[1] for w in s.split("\n") if w.find("\t")>0))
「ヌカニグギ」と結果が表示されます。
スクリプトを見て特に疑問がわかない方は以下を読まなくても大丈夫です。
丁寧な説明
まずMeCabを導入します
! pip install -U mecab-python3 unidic-lite
import MeCab
わかります。
MeCabで「糠に釘」を形態素解析し、結果を表示してみる
tagger = MeCab.Tagger()
s = tagger.parse("糠に釘")
print(s)
# ----結果----
# 糠 ヌカ ヌカ 糠 名詞-普通名詞-一般 2,0
# に ニ ニ に 助詞-格助詞
# 釘 クギ クギ 釘 名詞-普通名詞-一般 0
# EOS
わかります。
「糠に釘」のヨミガナを表示する
print("".join(w.split()[1] for w in s.split("\n") if w.find("\t")>0))
# ----結果----
# ヌカニクギ
・・・わかりません。
確かに正しい結果は出ますが、何をやっているのかが全然わかりません。
精読作業
ということで、
print("".join(w.split()[1] for w in s.split("\n") if w.find("\t")>0))
の1行について、内容を一つ一つ丁寧に、徹底的に解読していきます。
まず、分析結果の文字列はsになるので、sの中身をみてみます。
s
# ----結果----
# '糠\tヌカ\tヌカ\t糠\t名詞-普通名詞-一般\t\t\t2,0\nに\tニ\tニ\tに\t助詞-格助詞\t\t\t\n釘\tクギ\tクギ\t釘\t名詞-普通名詞-一般\t\t\t0\nEOS\n'
形態素解析の結果は、データとしては\tの半角スペースと、\nの改行で構成されていることがわかります。
内包表記の処理について、一つずつ見ていきます。
print("".join(w.split()[1] for w in s.split("\n") if w.find("\t")>0))
s.split("\n")の部分で、先程のsの内容を"\n"を区切り記号として分割しています。
.split("区切り記号")というコマンドは、文字列を"区切り記号"で区切って、それをリストとして格納する処理を行います。
なのでs.split("\n") のこの時点での中身は、
['糠\tヌカ\tヌカ\t糠\t名詞-普通名詞-一般\t\t\t2,0',
'に\tニ\tニ\tに\t助詞-格助詞\t\t\t',
'釘\tクギ\tクギ\t釘\t名詞-普通名詞-一般\t\t\t0',
'EOS',
'']
という、ここでは1次元5要素のリスト(配列)となっています。
(そもそもリストがわからないという場合はこちらの記事などが参考になります。)
次に、
print("".join(w.split()[1] for w in s.split("\n") if w.find("\t")>0))
を解釈します。
for w inは、s.split("\n")のリストの中身を変数wに一つずつ代入していく処理です。
(そもそも「for文とリストの組み合わせ」がわからない場合はこちらの記事などが参考になります。)
ただしこの場合、後ろに、if w.find("\t")>0とあります。
これは、今入ったwを調べ、wに代入された文字列の中に"\t"が1つ以上見つかった場合に限り、w.split()の処理に回すという条件です。
つまり、s.split("\n") の中身について、"\t"を含まない最後の'EOS'と''の要素はここでは処理にされず、排除されることになります。
これで、処理されるものは、
['糠\tヌカ\tヌカ\t糠\t名詞-普通名詞-一般\t\t\t2,0',
'に\tニ\tニ\tに\t助詞-格助詞\t\t\t',
'釘\tクギ\tクギ\t釘\t名詞-普通名詞-一般\t\t\t0',
の3要素に絞られています。
この3要素、に対して、以下の処理が行われます。
print("".join(w.split()[1] for w in s.split("\n") if w.find("\t")>0))
w.split()は w.split("\t") と近しい意味で、スペース区切りの配列として単語ごとに配列格納されていきます。
たとえば最初の文字列である
'糠\tヌカ\tヌカ\t糠\t名詞-普通名詞-一般\t\t\t2,0'
は、この処理によって、
[ '糠', 'ヌカ', 'ヌカ', '糠', '名詞-普通名詞-一般', '2,0' ]
のように分割され、リストとして格納されます。
ここでなぜ、w.split("\t")ではなく、w.split()とするのでしょうか。
実は.split()は"\t"のような引数を指定しないことで空白全般を分割記号の対象にできる機能を持ちます。
.split()とした場合に分割記号とみなされるのは、具体的には半角スペースに加え、タブ(\t)、改行(\n)、キャリッジリターン(\r)、改ページ(\f)などです。
またこれらが連続した場合には、1スペースとしてひとまとめにした扱いとなるので、結果が安定しやすくなります。
print("".join(w.split()[1] for w in s.split if w.find("\t")>0))
インデックス指定の[1]があるので、いま作成されたリストである
[ '糠', 'ヌカ', 'ヌカ', '糠', '名詞-普通名詞-一般', '2,0' ]
について、1番目の要素がピックアップされます。要素は0番から数えるので、ここでは 'ヌカ' が選ばれることになります。
print("".join(w.split()[1] for w in s.split("\n") if w.find("\t")>0))
その 'ヌカ' が、"".join()として処理されます。
.join()はリストの中の文字列を連結していく処理です。
.join()の書式は、
' 間に挿入する文字列 ' .join ( [ 連結したい文字列のリスト ] )
です。
なので、さきほどピックアップしたデータの各行の1番目にある「読みのカタカナ表記」が、連結され出力されます。
この一連の作業は、
print("".join(w.split()[1] for w in s.split("\n") if w.find("\t")>0)
の回数だけ繰り返されます。
s.split("\n") の中身は5要素ありますので、5回分文字列の連結が繰り返されます。
(ただし、前述の通り、"\t"を含まない要素は処理をキャンセルされるので、実際に連結されるのは3要素となります。)
ちなみに
ヨミガナの結果を得るには、fugashiライブラリを使うのが良いようです。
Colab環境でfugashiを使う場合についても、コンパクトにまとめてみました。
- [Colab環境でfugashiを使ってヨミガナを表示する5行]
(https://qiita.com/Ninagawa_Izumi/items/8f582a4f670e406ee0b9)