見出し画像

Pythonライブラリ(形態素解析):janome

1.概要

 文章を形態素(意味をもつ表現要素の最小単位)に分割してくれるjanomeを紹介します。janomeは日本語を解析することができます。
 自然言語処理(人の言語を機械学習)において単語に意味を持たせるため、形態素解析は前処理として使用されたりします。

1-1.テキスト解析の基礎

 テキスト解析に関して概要を「テキスト解析(スライド・東京大学)(CC-BY)」から抜粋しました。各種用語の説明があります。

2.環境構築

 janomeの環境構築はpipでライブラリをインストールするだけです。

[Terminal]
pip install janome

3.形態素解析:Tokenizer()

 本章では基礎操作を紹介します。APIは公式Docs参照しました。

3-1.形態素・品詞確認

 最もシンプルに形態素解析する場合はjanomeのインスタンスを作成して文章を入れるだけです。

[IN]
from janome.tokenizer import Tokenizer
t = Tokenizer()

text = 'すもももももももものうち' #文章

for token in t.tokenize(text):
    print(token)
    
print(type(next(iter(t.tokenize(text))))) #型式の確認用

[OUT]
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
うち	名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ

<class 'janome.tokenizer.Token'>

 出力はjanomeクラスのため必要な情報の抽出は指定のメソッド/プロパティを使用します。主要なプロパティは下記の通りです。

【tokenのプロパティ】
surface:単語
part_of_speech:品詞情報

[IN]
from janome.tokenizer import Tokenizer
t = Tokenizer()

text = 'すもももももももものうち' #文章
token = next(iter(t.tokenize(text))) #1個だけ取得

print(token)
print('token.surface:', token.surface)
print('token.part_of_speech:', token.part_of_speech)
print('token.infl_type:', token.infl_type)
print('token.infl_form:', token.infl_form)
print('token.base_form:', token.base_form)
print('token.reading:', token.reading)
print('token.phonetic:', token.phonetic)

[OUT]
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
token.surface: すもも
token.part_of_speech: 名詞,一般,*,*
token.infl_type: *
token.infl_form: *
token.base_form: すもも
token.reading: スモモ
token.phonetic: スモモ

3-2.分かち書きモード(単語のみ抽出):wakati

 品詞情報は不要であり単語だけ抽出したい場合は分かち書きモードを使用します。本モードは"t.tokenize(wakati=True)"とするか、毎回分かち書きモードにするならインスタンス化のときに"wakati=True"を渡します。
 分かち書きモードの時は各出力はstr型となります。

[IN]
from janome.tokenizer import Tokenizer
t = Tokenizer()

text = 'すもももももももものうち' #文章

for token in t.tokenize(text, wakati=True): #分かち書きモード
    print(token)
    
print(type(next(iter(t.tokenize(text, wakati=True))))) #型式の確認用

[OUT]
すもも
も
もも
も
もも
の
うち

<class 'str'>
[IN]
from janome.tokenizer import Tokenizer
t = Tokenizer(wakati=True) #分かち書きモード

text = 'すもももももももものうち' #文章
print(list(t.tokenize(text))) #分かち書きモードで出力

[OUT]
['すもも', 'も', 'もも', 'も', 'もも', 'の', 'うち']

3-3.サンプルコード

 テキストを複数用意してどのように形態素解析されるか確認しました。全部ひらがなだと精度が低いですが、漢字も合わせた文章では比較的よい結果が得られました。

[IN]
from janome.tokenizer import Tokenizer
import pandas as pd

texts = [] #文章を格納するリスト
texts.append('赤巻紙青巻紙黄巻紙')
texts.append('あかまきがみあおまきがみきまきがみ')
texts.append('カエルぴょこぴょこ三ぴょこぴょこあわせてぴょこぴょこ六ぴょこぴょこ')
texts.append('かえるぴょこぴょこみぴょこぴょこあわせてぴょこぴょこむぴょこぴょこ')
texts.append('骨粗鬆症訴訟勝訴')
texts.append('こつそしょうしょうそしょうしょうそ')
texts.append('新人歌手新春シャンソンショー')
texts.append('しんじんかしゅしんしゅんしゃんそんしょー')
texts.append('スモモも桃も桃のうち 桃もスモモももものうち')
texts.append('すもももももももものうち もももすももももものうち')
texts.append('東京特許許可局長今日急遽休暇許可拒否')
texts.append('とうきょうとっきょきょかきょくちょうきょうきゅうきょきゅうかきょかきょひ')
texts.append('隣の客はよく柿食う客だ')
texts.append('となりのきゃくはよくかきくうきゃくだ')
texts.append('生麦生米生卵')
texts.append('なまむぎなまごめなまたまご')
texts.append('にわの庭には二羽の鶏は鰐を食べた')
texts.append('にわのにわにはにわのにわとりはわにをたべた')
texts.append('庭には鶏が二羽いました')
texts.append('にわにはにわとりがにわいました')
texts.append('坊主が屏風に上手に坊主の絵を描いた')
texts.append('ぼうずがびょうぶにじょうずにぼうずのえをかいた')

outputs = [] #出力するリスト
t = Tokenizer(wakati=True)
for text in texts:
    outputs.append(list(t.tokenize(text)))
        
pd.DataFrame(outputs).to_csv('janome_wakati.csv',index=False, header=False, encoding="shift-jis") #Excelに出力

[OUT]

4.形態素分析:Analyzer()

 形態素解析した結果を分析するのはAnalyzer()を使用します。

4-1.指定品詞の抽出:POSKeepFilter

 指定品詞を抽出する場合はPOSKeepFilter([<抽出したい品詞>])をAnalyzerのtoken_filtersに渡します。

[IN]
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.tokenfilter import *

text = 'すもももももももものうち'
token_filters = [POSKeepFilter(['名詞'])]

for token in Analyzer(token_filters=token_filters).analyze(text):
    print(token)

[OUT]
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
うち	名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ

4-2.品詞数のカウント:TokenCountFilter()

 指定した品詞の数をカウントするためにはtoken_filtersに指定のモジュールを渡します。出力は文字列(str)と数値(int)で出力されます。

[IN]
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.tokenfilter import *

text = 'すもももももももものうち'
token_filters = [POSKeepFilter(['名詞']), TokenCountFilter()]

a = Analyzer(token_filters=token_filters)
for k, v in a.analyze(text):
  print(k, v)

[OUT]
すもも 1
もも 2
うち 1

 存在しない品詞を選んだ場合は空リストで出力されます。

[IN]
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.tokenfilter import *

text = 'すもももももももものうち'
token_filters = [POSKeepFilter(['名詞']), TokenCountFilter()]
token_filters2 = [POSKeepFilter(['助詞']), TokenCountFilter()]
token_filters3 = [POSKeepFilter(['動詞']), TokenCountFilter()]

print('名詞', list(Analyzer(token_filters=token_filters).analyze(text)))
print('助詞', list(Analyzer(token_filters=token_filters2).analyze(text)))
print('動詞', list(Analyzer(token_filters=token_filters3).analyze(text)))

[OUT]
名詞 [('すもも', 1), ('もも', 2), ('うち', 1)]
助詞 [('も', 2), ('の', 1)]
動詞 []

5.janomeで遊んでみた:文豪の文章可視化

 janomeで文豪がよく使用する文字の可視化を実施しました。

5-1.サンプルデータの取得:青空文庫

 今回のサンプルデータは青空文庫から「太宰治 人間失格」を使用しました。こちらのデータはBeautiful Soupを使用してスクレイピングしました。
 なおjanomeで処理させるため不要な文字コードはclean_text()関数を作成して除去しました。

[IN]
import requests
from bs4 import BeautifulSoup

url ='https://www.aozora.gr.jp/cards/000035/files/301_14912.html' #青空文庫_太宰治:人間失格
res = requests.get(url)
soup = BeautifulSoup(res.content, 'html5lib')
# print(soup.prettify()) #構造を確認

def clean_text(text):
    text = text.replace('\u3000', '') #全角スペースを消す
    text = text.replace('\n', '') #改行を消す
    return text

_main_text = soup.select_one('.main_text')
main_text = clean_text(_main_text.text)
main_text 

[OUT]
'はしがき私は、その男の写真を三葉、見たことがある。一葉は、その男の、幼年時代、とでも言うべきであろうか、十歳前後かと推定される頃の写真であって、その子供が大勢の女のひとに取りかこまれ、(それは、その子供の姉たち、妹たち、それから、従姉妹(いとこ)たちかと想像される)庭園の池のほとりに、荒い縞の袴(はかま)をはいて立ち、首を三十度ほど左に傾け、醜く笑っている写真である。醜く?けれども、鈍い人たち(つまり、美醜などに関心を持たぬ人たち)は、面白くも何とも無いような顔をして、「可愛い坊ちゃんですね」といい加減なお世辞を言っても、まんざら空(から)お世辞に聞えないくらいの、謂(い)わば通俗の「可愛らしさ」みたいな影もその子供の笑顔に無いわけではないのだが、しかし、いささかでも、美醜に就いての訓練を経て来たひとなら、ひとめ見てすぐ、「なんて、いやな子供だ」と頗(すこぶ)る不快そうに呟(つぶや)き、毛虫でも払いのける時のような手つきで、その写真をほうり投げるかも知れない。まったく、その子供の笑顔は、よく見れば見るほど、何とも知れず

5-2.名詞のみ抽出

 可視化にはWordCloudを使用します。文章をそのまま渡すと"てにをは"などの助詞が多く出現するため傾向が見えにくいです。そこでAnalyzer()を使用して名詞のみ抽出しました。
 なおWordCloudが処理できるように名詞間にはスペースを追加しました。

[IN]
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.tokenfilter import *

text_output = '' 

token_filters = [POSKeepFilter(['名詞'])]
for token in Analyzer(token_filters=token_filters).analyze(main_text):
    text_output += ' ' + token.surface #単語間にスペースを入れる

print(text_output)

[OUT]
 はし 私 男 写真 三 葉 こと 一葉 男 幼年 時代 十 歳 前後 推定 頃 写真 子供 大勢 女 ひと それ 子供 姉たち 妹 たち 従姉妹 いとこ たち 想像 庭園 池 ほとり 縞 袴 はかま 首 三 十 度 左 写真 人 たち 美醜 関心 人 たち よう 顔 坊ちゃん 世辞 空 世辞 謂 い 通俗 さ みたい 影 子供 笑顔 わけ の 美醜 訓練 ひと ひと め いや 

5-3.可視化:WordCloud

 最後にWordCloudで可視化します。名詞のみ抽出することでキーワードが明確になりました。”これ”、”それ”などの不要な文字も抜いていけば文章内で重要なワードが見えるようになるかもしれません。

[IN]
import matplotlib.pyplot as plt
import japanize_matplotlib

fig = plt.figure(figsize=(16, 12))
plt.rcParams["font.size"] = 20 #フォントサイズを修正
ax1 = fig.add_subplot(121) #subplot(行列後のindex)は0ではなく1から開始
ax2 = fig.add_subplot(122) 

#前処理なしの通常の文章
wc_origin = WordCloud(width=1200, height=720, 
                      background_color='white', 
                      font_path='meiryo.ttc').generate(main_text)

#名詞のみを抽出した文章
wc_noun = WordCloud(width=1200, height=720, 
                      background_color='white', 
                      font_path='meiryo.ttc').generate(text_output)

ax1.imshow(wc_origin)
ax1.axis('off') #軸の表示を消す
ax1.set_title('オリジナル')
ax2.imshow(wc_noun)
ax2.axis('off') #軸の表示を消す
ax2.set_title('名詞のみ抽出')
plt.show()

[OUT]

参考資料


あとがき

 どこかで書いた気がしていたがwordcloudで少し書いてた・・・


この記事が気に入ったらサポートをしてみませんか?