jiebaで中国語テキストを形態素解析する
こんにちは。データムスタジオの林です。学生時代は数学科で、プログラミングでモデルを作るのが好きです。
近年、中国市場の需要が活発する中、データ分析で中国語データに触れる機会が増えていくと感じられる方いらっしゃいませんか?というわけで、今回は中国語自然言語処理の基本について紹介したいと思います。
この記事では、Pythonのjiebaというパッケージを用いて、台湾の人気掲示板における投稿タイトルを形態素に区切り、tf-idfウェイトを算出し、そして最後にウェィトの最も高い単語のワードクラウドを描くことで、台湾で今人気な日本観光地を発見します。
では、早速始めましょう!
目次
1. 事前準備
この記事はjieba以外に、nltk、sklearn、wordcloud等パッケージも使いますが、文を長引かせないため、本題にフォーカスします。各パッケージにご興味の方は記事の最後に乗せたコードをご参照ください。
さて、中国語の自然言語処理と言えば、実はパッケージが色々ありますが、jiebaはその中で最も使いやすいのかもしれません。
1.1 まずはjiebaをインストール
$ pip install jieba
1.2 次は繁体字対応辞書をダウンロードして、jiebaのディレクトリに保存する
該当するディレクトリは下記コマンドで取得できます:
import jieba print(jieba.__file__)
自分の場合はjieba/data/dict.txt.bigに保存しました。
1.3 最後は中国語のstop wordリストをダウンロードして、同じ場所に保存する
こちらはjieba/data/stop_words.txtに保存しました。
これで準備完了です。では、必要になるパッケージを全部インポートしましょう:
from operator import itemgetter from itertools import chain import numpy as np import pandas as pd import jieba from nltk.corpus import stopwords from sklearn.feature_extraction.text import TfidfVectorizer import matplotlib.pyplot as plt from PIL import Image from wordcloud import WordCloud, ImageColorGenerator from googletrans import Translator from progressbar import progressbar
2. データ読み込み
日本の人気観光地を発見するため、台湾のポピュラーな掲示板「PTT」(※)における日本旅行板より、最近の投稿を9000件余りクローラで取得しました。今回はそれのタイトルを使って分析します。実際見てみればこんな感じになります:
df = pd.read_csv('Japan_Travel.csv')
corpus = df.title
print(corpus.head(10))| 0 | [遊記] 靜岡-秘境車站奧大湖井上車站 |
| 1 | [遊記] 2015 河口湖/箱根遊記 Day2 |
| 2 | [遊記] 沖繩 Birthday 港川店 好逛嬰幼兒用品店 |
| 3 | [問題] 岡山、廣島等第一次旅遊行程 |
| 4 | 請問最近有去日本的好心人 |
| 5 | [遊記] 神宮外苑銀杏大道、Shake Shack漢堡 |
| 6 | [遊記] 東京 谷根千一日散策。樸實的下町風情 |
| 7 | [心得] 沖繩5家租車資訊大匯整 (含比較表格) |
| 8 | [問題]11月底東京行程請教(銀杏/楓葉) |
| 9 | [遊記] 函館夜景 函館山百萬夜景 日本三大夜景 |
※日本の2ちゃんねるのような電子掲示板です。
3. 形態素解析
次は今回の本題、中国語テキストの区切りに入ります。それにまずすべきとこは:
# デフォルト辞書を繁体字対応辞書に入れ替える
jieba.set_dictionary('/path/to/jieba/data/dict.txt.big')
# 中国語と英語それぞれのstop wordsを読み込む
ch_stopwords = pd.read_csv('/path/to/jieba/data/stop_words.txt', header=None)[0].tolist()
en_stopwords = stopwords.words('english')
# ユーザー定義単語を読み込んで、辞書に追加
userdict = pd.read_csv('userdict.txt', header=None)[0].tolist()
for w in userdict:
jieba.add_word(w)jiebaのデフォルト辞書を先ほど保存した辞書に入れ替えた理由は、デフォルト辞書では繁体字サポートがいまいちからです。それに、時にタイトルに英語も現れるため、中国語だけではなく、英語stop wordsも読み込んでおきます(ここはnltkが作成してくれたリストを使います)。最後はjiebaの
jieba.add_word(string)
という関数で、辞書に含まれていない単語も認識させます。
続いては中国語文字列の区切りです:
# コーパスを前処理する
def preprocess_text(text):
tokens = jieba.cut(text)
lower_alpha = [t.lower() for t in tokens if t.isalpha()]
no_stops = [t for t in lower_alpha if t not in set(ch_stopwords)&set(en_stopwords)]
return ','.join(no_stops)
processed = corpus.apply(preprocess_text)上記コードのように、jiebaで中国語文字列を区切るには
jieba.cut(string)
という関数を使います。文字列を渡すとgeneratorが戻されます。ちなみに、ここ前処理では、約物を除外し小文字に変換した上、stop wordsを取り除きました。
4. ベクトルへ変換、tf-idfの最も高い単語を抽出
# TfidfVectorizerを作成 vec = TfidfVectorizer() # 前処理したコーパスをベクトル化する corpus_vec = vec.fit_transform(corpus_processed) # tf-idfウェィトの最も高い単語を抽出 tokens = vec.get_feature_names() keyword_idx = [row[-3:][::-1] for row in corpus_vec.toarray().argsort()] nonzero_idx = [row.nonzero()[0] for row in corpus_vec.toarray()] keyword_idx = [set(k)&set(n) for k, n in zip(keyword_idx, nonzero_idx)] keyword_idx = [i for i in keyword_idx if len(i) != 0] keywords = [itemgetter(*i)(tokens) for i in keyword_idx] keywords = list(chain(*keywords))
形態素を手にしたなら、既に山を越えました。ここはsklearnのTfidfVectorizer()を使って形態素をベクトルに変換して、投稿別にtf-idfの最も高い単語を抽出しました。
5. ワードクラウドを描く
現在台湾で最も人気な日本観光地を一目でわかるようにするため、ワードクラウドを描きました:
# ワードクラウドを描く
plt.rcParams['font.family'] = 'LiHei Pro'
mask = np.array(Image.open('sakura.png'))
wc = WordCloud(mask=mask, scale=10, min_font_size=5, max_words=300, background_color='black', font_path='/path/to/LiHeiPro.ttf')
wc.generate(','.join(keywords))
image_colors = ImageColorGenerator(mask)
wc.recolor(color_func=image_colors)
wc.to_file('wc.png')ここで注意すべきなのは、WordCloud()を作成時、font_pathというパラメータで中国語フォントのパスを渡さないと、ワードクラウドに文字化けが発生してしまいます。
アウトプット:
![]()

6. おまけ:日本語バージョン
中国語だとわからない単語もあるかもしれないので、一応Google翻訳のAPIを呼び出し日本語に翻訳させて、日本語バージョンも描きました:
# 日本語バージョンを描く
def to_jp(string):
while True:
translator = Translator()
try:
trans = translator.translate(string, src='zh-TW', dest='ja').text
except:
continue
return trans
plt.rcParams['font.family'] = 'IPAexGothic'
keywords_jp = [to_jp(k) for k in progressbar(keywords)]
wc = WordCloud(mask=mask, scale=10, min_font_size=5, max_words=300, background_color='black', font_path='/path/to/IPAexGothic.ttf')
wc.generate(' '.join(keywords_jp))
image_colors = ImageColorGenerator(mask)
wc.recolor(color_func=image_colors)
wc.to_file('wc_jp.png')上記と同じように、ここは日本語フォントのパスをWordCloud()に渡しておきます。
アウトプット:

やはり東京、京都、北海道、大阪、沖縄、北九州等がとても人気ですね。個人旅行やセルフドライブもすごく流行っているようです。よく見るともっと具体的な観光地も見えます。
7. まとめ
いかがでしょうか?jiebaを使えば、中国語の形態素解析も意外とそんなに難しくないかもしれませんね。これで日本の旅行会社の参考になれたら嬉しいです(笑)。
では、また次回!
【ソースコード】
from operator import itemgetter
from itertools import chain
import numpy as np
import pandas as pd
import jieba
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import TfidfVectorizer
import matplotlib.pyplot as plt
from PIL import Image
from wordcloud import WordCloud, ImageColorGenerator
from googletrans import Translator
from progressbar import progressbar
# コーパスを読み込む
df = pd.read_csv('Japan_Travel.csv')
corpus = df.title
print(corpus.head(10))
# デフォルト辞書を繁体字対応辞書に入れ替える
jieba.set_dictionary('/path/to/jieba/data/dict.txt.big')
# 中国語と英語それぞれのstop wordsを読み込む
ch_stopwords = pd.read_csv('/path/to/jieba/data/stop_words.txt', header=None)[0].tolist()
en_stopwords = stopwords.words('english')
# ユーザー定義単語を読み込んで、辞書に追加
userdict = pd.read_csv('userdict.txt', header=None)[0].tolist()
for w in userdict:
jieba.add_word(w)
# コーパスを前処理する
def preprocess_text(text):
tokens = jieba.cut(text)
lower_alpha = [t.lower() for t in tokens if t.isalpha()]
no_stops = [t for t in lower_alpha if t not in set(ch_stopwords)&set(en_stopwords)]
return ','.join(no_stops)
corpus_processed = corpus.apply(preprocess_text)
# TfidfVectorizerを作成
vec = TfidfVectorizer()
# 前処理したコーパスをベクトル化する
corpus_vec = vec.fit_transform(corpus_processed)
# tf-idfウェィトの最も高い単語を抽出
tokens = vec.get_feature_names()
keyword_idx = [row[-3:][::-1] for row in corpus_vec.toarray().argsort()]
nonzero_idx = [row.nonzero()[0] for row in corpus_vec.toarray()]
keyword_idx = [set(k)&set(n) for k, n in zip(keyword_idx, nonzero_idx)]
keyword_idx = [i for i in keyword_idx if len(i) != 0]
keywords = [itemgetter(*i)(tokens) for i in keyword_idx]
keywords = list(chain(*keywords))
# ワードクラウドを描く
plt.rcParams['font.family'] = 'LiHei Pro'
mask = np.array(Image.open('sakura.png'))
wc = WordCloud(mask=mask, scale=10, min_font_size=5, max_words=300, background_color='black', font_path='/path/to/LiHeiPro.ttf')
wc.generate(','.join(keywords))
image_colors = ImageColorGenerator(mask)
wc.recolor(color_func=image_colors)
wc.to_file('wc.png')
# 日本語バージョンを描く
def to_jp(string):
while True:
translator = Translator()
try:
trans = translator.translate(string, src='zh-TW', dest='ja').text
except:
continue
return trans
plt.rcParams['font.family'] = 'IPAexGothic'
keywords_jp = [to_jp(k) for k in progressbar(keywords)]
wc = WordCloud(mask=mask, scale=10, min_font_size=5, max_words=300, background_color='black', font_path='/path/to/IPAexGothic.ttf')
wc.generate(' '.join(keywords_jp))
image_colors = ImageColorGenerator(mask)
wc.recolor(color_func=image_colors)
wc.to_file('wc_jp.png')