Pythonでwordcloudを作ってみた。


#はじめに
初学者ですが、Pythonの練習がてらワードクラウドであの印象的な絵を自分も書いてみたい！と思い楽しんだ。
その作業内容を備忘録的に書き残しておく。

#作業環境
作業環境は、Ubuntu18.04.4 LTS
Python 3.6.9
mecab-python3 0.996.5

本記事のソースコード内にある、ファイル引数などは適宜ご自身の環境に読み替えてください。

#WordCloudとは
WordCloudは、文章中で出現頻度が高い単語を複数選び出し、その頻度に応じた大きさで図示する手法。ウェブページやブログなどに頻出する単語を自動的に並べることなどを指す。文字の大きさだけでなく、色、字体、向きに変化をつけることで、文章の内容をひと目で印象づけることができる。(デジタル大辞泉の解説より)

今回最終的に完成したワードクラウドの図はこれ。
テキストはアップル創業者であるスティーブ・ジョブズのスピーチのものを別途作成し、入力ファイルとして渡した。

また、マスク画像を使用して、ジョブズとアップルロゴの輪郭内に文字列が表示されるよにした。

自分、人生、好き、大学などの文字が目立ちますね。
個人的にはかっこよくできたと思うので満足です:clap:

![wc_image_ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/372856/0945bd4b-b204-6aa4-6d84-9a8103cbca81.png)


#WordCloudを作成

上の画像を作成するために最終的に完成したソースコードを載せておく。

```python:sample4wordcloud.py
#coding: utf-8
from PIL import Image
import numpy as np
from matplotlib import pyplot as plt
from wordcloud import WordCloud
import requests
import MeCab

#ワードクラウド作成関数(英語テキスト版)
def create_wordcloud_en(text, image):
    fontpath = 'NotoSansCJK-Regular.ttc'
    stop_words_en = [u'am', u'is', u'of', u'and', u'the', u'to', u'it', \
                  u'for', u'in', u'as', u'or', u'are', u'be', u'this', u'that', u'will', u'there', u'was']

    wordcloud = WordCloud(background_color="white",
                          font_path=fontpath,
                          width=900,
                          height=500,
                          mask = msk,
                          contour_width=1,
                          contour_color="black",
                          stopwords=set(stop_words_en)).generate(text)

    #描画
    plt.figure(figsize=(15,20))
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.show()
    #png出力
    wordcloud.to_file("wc_image_en.png")

#ワードクラウド作成関数(日本語テキスト版)
def create_wordcloud_ja(text, image):
    fontpath = 'NotoSansCJK-Regular.ttc'
    stop_words_ja = ['もの', 'こと', 'とき', 'そう', 'たち', 'これ', 'よう', 'これら', 'それ', 'すべて']
    #形態素解析
    tagger = MeCab.Tagger() 
    tagger.parse('') 
    node = tagger.parseToNode(text)

    word_list = []
    while node:
        word_type = node.feature.split(',')[0]
        word_surf = node.surface.split(',')[0]
        if word_type == '名詞' and word_surf not in stop_words_ja:
            word_list.append(node.surface)
        node = node.next

    word_chain = ' '.join(word_list)
    wordcloud = WordCloud(background_color="white",
                          font_path=fontpath,
                          width=900,
                          height=500,
                          mask = msk,
                          contour_width=1,
                          contour_color="black",
                          stopwords=set(stop_words_ja)).generate(word_chain)

    #描画
    plt.figure(figsize=(15,20))
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.show()
    wordcloud.to_file("wc_image_ja.png")


#必要ファイルの呼び出し
#テキストの読み込み
with open('jobs.txt', 'r', encoding='utf-8') as fi:
    text = fi.read()
#使用するマスク画像の読み込み
msk = np.array(Image.open("apple.png"))

create_wordcloud_ja(text, msk)

```
ワードクラウドを作成する関数を、日本語テキスト用と英語テキスト用で２つ定義している。
似たようなコードを書いてしまっているので、きっとここはもっとスマートにかけるはず...
クラス使って書けば良いのかな？


###ソースコードについて
英語テキストと日本語テキストで、ワードクラウドを描くまでの処理の仕方が異なる。

英語は、「I like Apple.」のように、スペースで単語ごとに別れているので、品詞に分ける際に区切れがどこかわからなくなることはない。
しかし、日本語の場合「私はアップルが好きです。」のように区切れが明白ではない。

よって、日本語の場合文字列を分けるために形態素解析を行う必要がある。
今回は、MeCabを使用して、形態素解析を行った。


####形態素解析のコードの大まかな解説

```python:sample.py
    tagger = MeCab.Tagger()
    tagger.parse('') 
    node = tagger.parseToNode(text) 

    word_list = []
    while node:
        word_type = node.feature.split(',')[0]
        word_surf = node.surface.split(',')[0]
        if word_type == '名詞' and word_surf not in stop_words_ja:
            word_list.append(node.surface)
        node = node.next

```
上記が、形態素解析を行っている部分。

```python
tagger = MeCab.Tagger()
```
出力モードの設定。
引数の設定を変えると出力モードも変わる。

- "-Ochasen":(ChaSen互換形式)
- "-Owakati":(分かち書きのみ出力)
- "Oyomi":(読みのみを出力)

引数がすべてOから始まっていて可愛いですね(笑)

```python
tagger.parse('')
```
この部分はいまいち理解していないのですが、
パーサーにデータを渡す前にこれを記述しておくことでUnicodeDecodeErrorを避けられるそう...

```python
node = tagger.parseToNode(text)
```
nodeにsurface(単語), feature(品詞情報)を持つ解析結果を代入。
node.surfaceやnode.featureと書くとそれぞれにアクセスできる。


```python
    word_list = []
    while node:
        word_type = node.feature.split(',')[0]
        word_surf = node.surface.split(',')[0]
        if word_type == '名詞' and word_surf not in stop_words_ja:
            word_list.append(node.surface)
        node = node.next

    word_chain = ' '.join(word_list)
```
nodeひとつひとつを順番に読み取り、品詞が名詞でかつ、stop_words_jaにないものをword_listに追加する。

そして区切り文字を空白にして、リストを文字列に変換して、word_chainを得る。

###ソース修正前の図

実は、最初に描いた絵は、非表示の文字を設定せずに名詞全てから出力していました。
そうするとこんな感じ、、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/372856/8a26a864-3630-1df9-a8f9-e91f0dfcdcf1.png)

この図では、’こと’、’それ’、’よう’など、あまり表示されても面白みを感じない文字列が目立ちます。

これはなんかださい...:frowning2:

ということで、表示させないワードリストを作り、表示されてほしくないような文字列を表示させないようにしました。


#最後に
久々にPythonを触ってみました。
やっぱり楽しいですね〜:relaxed:
次は、SNSをスクレイピングして遊んでみようかなと思ってます。
この記事の内容で間違っている部分やアドバイス等ございましたら、ご教授ください。


#参考にした記事・関連記事
https://sleepless-se.net/2018/08/24/python-mecab-wakatigaki/

https://qiita.com/furipon308/items/be97abf25cf4caa0574e

https://qiita.com/yonedaco/items/27e1ad19132c9f1c9180

https://analysis-navi.com/?p=2295