【WordCloud入門】Jetson-nanoでも簡単に使えるね♬

WordCloud一度はみたことあると思うけど、やってみたら簡単だったので記事にして置こうと思う。
こんな感じのものが作れます。
![4-yosino_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/2fe02643-d82b-1030-748d-53315cf21060.png)
![5-yosino_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/659c7fda-1f3b-a6fe-f967-9130ea307d15.png)

###やったこと
・環境
・一番簡単な例
・ちょっとこってみる
・利用を考える
###・環境
今回は、Jetson-nanoを利用した。したがって、[ベースとなる環境はリンクのとおり](https://qiita.com/MuAuan/items/9b2b63d12e094ad2e739)、つまりUbuntu環境である。
通常は、以下でインストール出来る。

```
$ pip3 install wordcloud
```
だが、いくつかエラーが出てインストールできなかったので、

```
$ sudo pip3 install wordcloud
```
でインストールできた。
日本語の場合はその他に、分かち書きと品詞の分析をするために[MeCabなどをインストールする](https://qiita.com/MuAuan/items/d8bfaf6eb82ef59a3e80)必要がある。また、日本語フォントを以下の参考②のとおり、インストールした。
まず、[リンクから](https://www.google.com/get/noto/)Noto Sans CJK JP をダウンロードしておきます。

```
$ unzip NotoSansCJKjp-hinted.zip
$ mkdir -p ~/.fonts
$ cp *otf ~/.fonts
$ fc-cache -f -v # optional
```
【参考】
①[amueller/word_cloud](https://github.com/amueller/word_cloud)
②[【備忘録】日本語のワードクラウドを作る](https://qiita.com/TkrUdagawa/items/aa483630b5ec7d337c9e)
###・一番簡単な例
WordCloudは以下の参考のコードを見ると、文字頻度に応じてランダムにある領域に文字の大きさや出力方向をいろいろ変化させて出力するものであるようだ。
【参考】
・[word_cloud/wordcloud/wordcloud.py](https://github.com/amueller/word_cloud/blob/master/wordcloud/wordcloud.py)
ということで、一番簡単な利用コードは以下のようなものである。

```py
from MeCab import Tagger
import matplotlib.pyplot as plt
from wordcloud import WordCloud

t = Tagger()

text = "名城大（名古屋市）は25日、リチウムイオン電池の開発でノーベル化学賞を受賞した同大学教授で旭化成名誉フェローの吉野彰さん（72）に「特別栄誉教授」の称号を授与した。吉野さんは2017年から、大学院理工学研究科の教授を務めており、週1回の講義を受け持っている。名城大によると、特別栄誉教授はノーベル賞を受賞した教員などをたたえるための称号。14年に終身教授の赤崎勇さんと元教授の天野浩さんが、青色発光ダイオード（LED）の開発でノーベル物理学賞を受賞したことをきっかけに創設した。"

splitted = " ".join([x.split("\t")[0] for x in t.parse(text).splitlines()[:-1]])
print("1",splitted)
wc = WordCloud(font_path="/home/muauan/.fonts/NotoSansCJKjp-Regular.otf")
wc.generate(splitted)
plt.axis("off")
plt.imshow(wc)
plt.pause(1)
plt.savefig('./output_images/yosino0_{}.png'.format(text[0])) 
plt.close()
```
このコードのt = Tagger()の部分を変更して動かすと以下の表のWord Cloudの欄のものが生成できる。
上から、同じ項番が対応する。

|項番  | 辞書　 | 分かち書き＆品詞削除|
|:------------------------:|:--------------------------|:------------------------------------:|
|０|t = Tagger()|splitted = " ".join([x.split("\t")[0] for x in t.parse(text).splitlines()[:-1]])|
|１|t = Tagger(" -d " + args.dictionary)|splitted = " ".join([x.split("\t")[0] for x in t.parse(text).splitlines()[:-1]])|
|２|t = Tagger(" -d " + args.dictionary)|splitted = " ".join([x.split("\t")[0] for x in t.parse(text).splitlines()[:-1] if x.split("\t")[1].split(",")[0] not in ["助詞", "助動詞", "副詞", "連体詞", "動詞"]])|

|項番|辞書  | 分かち書き＆品詞削除　 | Word Cloud |
|:---|:------------------------:|:--------------------------|:------------------------------------:|
|０|default dictionary| 名城大 （ 名古屋 市 ） は 25 日 、 リチウム イオン 電池 の 開発 で ノーベル 化学 賞 を 受賞 し た 同 大学 教授 で 旭化成 名誉 フェロー の 吉野 彰 さん （ 72 ） に 「 特別 栄誉 教授 」 の 称号 を 授与 し た 。 吉野 さん は 2017 年 から 、 大学院 理工 学 研究 科 の 教授 を 務め て おり 、 週 1 回 の 講義 を 受け持っ て いる 。 名城大 に よる と 、 特別 栄誉 教授 は ノーベル 賞 を 受賞 し た 教員 など を たたえる ため の 称号 。 14 年 に 終身 教授 の 赤崎 勇 さん と 元 教授 の 天野 浩 さん が 、 青色 発光 ダイオード （ LED ） の 開発 で ノーベル 物理 学賞 を 受賞 し た こと を きっかけ に 創設 し た 。| ![yosino1_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/72c183b5-9ed6-00f7-241f-48aaf03a3bff.png)| 
|１|neologd| 名城大 （ 名古屋市 ） は 25日 、 リチウムイオン電池 の 開発 で ノーベル化学賞 を 受賞 し た 同 大学 教授 で 旭化成 名誉フェロー の 吉野彰 さん （ 72 ） に 「 特別栄誉教授 」 の 称号 を 授与 し た 。 吉野 さん は 2017年 から 、 大学院 理工学研究科 の 教授 を 務め て おり 、 週 1回 の 講義 を 受け持っ て いる 。 名城大 に よる と 、 特別栄誉教授 は ノーベル賞 を 受賞 し た 教員 など を たたえる ため の 称号 。 14年 に 終身 教授 の 赤崎勇 さん と 元 教授 の 天野浩 さん が 、 青色発光ダイオード （ LED ） の 開発 で ノーベル物理学賞 を 受賞 し た こと を きっかけ に 創設 し た 。| ![yosino2_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/ee9f8e6c-f298-7854-a50f-c29052fc38a6.png)| 
|２|neologd + 助詞、助動詞など削除| 名城大 （ 名古屋市 ） 25日 、 リチウムイオン電池 開発 ノーベル化学賞 受賞 同 大学 教授 旭化成 名誉フェロー 吉野彰 さん （ 72 ） 「 特別栄誉教授 」 称号 授与 。 吉野 さん 2017年 、 大学院 理工学研究科 教授 、 週 1回 講義 。 名城大 、 特別栄誉教授 ノーベル賞 受賞 教員 ため 称号 。 14年 終身 教授 赤崎勇 さん 元 教授 天野浩 さん 、 青色発光ダイオード （ LED ） 開発 ノーベル物理学賞 受賞 こと きっかけ 創設 。| ![yosino3_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/b68dbba4-dcb4-f957-b2cf-863c096d1f8d.png)| 
###・ちょっとこってみる
####コード
とりあえず、キーボード入力した文章をWordCloud生成するコードを以下に置いた。
[WordCloud/wc_input_original.py](https://github.com/MuAuan/WordCloud/blob/master/wc_input_original.py)
####ストップワードの導入
上記ではMecabで分かち書きした品詞を利用して不要な品詞の文字を削除した。しかし、これでもその文章を代表する文字だけを抽出しているかというと、まだまだ不十分で、さらに文字を削減してよりインパクトのある表示をしたい。
ということで、ストップワードを導入して特定の文字や文字列を削除することとする。
実現するための関数は以下のとおりである。
【参考】
③[TF-IDFで見る評価の高いラーメン屋の口コミ傾向（自然言語処理, TF-IDF, Mecab, wordcloud, 形態素解析、分かち書き）](https://www.takapy.work/entry/2019/01/14/142128#%E3%82%B9%E3%83%88%E3%83%83%E3%83%97%E3%83%AF%E3%83%BC%E3%83%89)
④[slothlib - Revision 77: /CSharp/Version1/SlothLib/NLP/Filter/StopWord/word配下のJapanese.txt](http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/)
削除したい文字列は上記のJapanese.txtをDirに置いて、そのファイルに追記すれば以下のexclude_stopword()関数を利用して削除出来る。

```py
# ストップワード読込関数
stop_words = []
if args.stop_words:
    for line in open(args.stop_words, "r", encoding="utf-8"):
        stop_words.append(line.strip())
    print(stop_words)

# リストを文字列に変換する関数
def join_list_str(list):
    return ' '.join(list)

# ストップワード除外関数
def exclude_stopword(text):
    changed_text = [token for token in text.lower().split(" ") if token != "" if token not in stop_words]
    # 上記のままだとリスト形式になってしまうため、空白区切の文字列に変換
    changed_text = join_list_str(changed_text)
    return changed_text
```
####WordCloudを工夫する
WordCloudを生成する関数は以下のとおりとした。
以下のコードは、以下の参考⑤と参考③と参考①を参考にしている。
【参考】
⑤[画像でマスク処理をしたWordcloud作成](https://qiita.com/myaun/items/0ef5c2e3ede10ee0c478)
簡単に説明すると、
・最初に日本語フォントを定義する
・引数skはファイル名を識別するために使う
・引数imgpathは、マスク機能を利用する場合この引数にマスク画像のファイルパスを入れる
・マスク利用する場合最初のif文以下を実行
・マスク利用しない場合はelse以下を実行する。こちらは、WordCloudの引数がほぼDefaltのもので、再定義できる（項目毎の説明はおまけに記載）

```py
fpath="/home/muauan/.fonts/NotoSansCJKjp-Regular.otf"

def get_wordcrowd_color_mask(sk, text, imgpath ):
    plt.figure(figsize=(6,6), dpi=200)
    if imgpath != "":
        img_color = np.array(Image.open( imgpath ))
        image_colors = ImageColorGenerator(img_color)
        wc = WordCloud(width=400,
                   height=300,
                   font_path=fpath,
                   mask=img_color,
                   collocations=False, # 単語の重複しないように
                  ).generate( text )
        plt.imshow(wc.recolor(color_func=image_colors), # 元画像の色を使う
               interpolation="bilinear")
    else:
        #wc = WordCloud(font_path=fpath, regexp="[\w']+").generate( text )
        wc = WordCloud(font_path=fpath, width=400, height=200, margin=2,
                 ranks_only=None, prefer_horizontal=.9, mask=None, scale=1,
                 color_func=None, max_words=200, min_font_size=4,
                 stopwords=None, random_state=None, background_color='black',
                 max_font_size=None, font_step=1, mode="RGB",
                 relative_scaling='auto', regexp=r"\w[\w']+" , collocations=True,
                 colormap=None, normalize_plurals=True, contour_width=0,
                 contour_color='black', repeat=False,
                 include_numbers=False, min_word_length=0).generate(text)
        plt.imshow(wc)
    # show
    plt.axis("off")
    plt.tight_layout()
    plt.pause(1)
    plt.savefig('./output_images/{}-yosino_{}.png'.format(sk,text[0])) 
    plt.close()
```
####生成結果
上記の関数を利用して以下のコードで生成する。
WordCloud生成したい文章をlineにコピペすることとした。
ストップワードなどの効果を見るために３段階の処理を実施した。
ここでは、一番最後の処理を以下記載し、その他はおまけ２に記載したので比較すると効果は明らかである。

```py
while True:
    line = input("> ")
    if not line:
        break
    splitted = " ".join([x.split("\t")[0] for x in t.parse(line).splitlines()[:-1] if x.split("\t")[1].split(",")[0] not in ["助詞", "助動詞", "副詞", "連体詞","接続詞","動詞","記号"]])
    splitted = exclude_stopword(splitted)
    print("2",splitted)
    get_wordcrowd_color_mask(4,splitted, '')
    get_wordcrowd_color_mask(5,splitted, './mask_images/alice_color.png')
```
![4-yosino_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/2fe02643-d82b-1030-748d-53315cf21060.png)
![5-yosino_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/659c7fda-1f3b-a6fe-f967-9130ea307d15.png)
###まとめ
・WordCloudにより文章の概要を表示できた
・ストップワードと品詞による制御により概要の精度が変化することを見た
・マスクを利用すると、ある領域に限って生成できることがわかる
・Jetson-nanoでも短時間に生成できる

・リアルタイム出力などを利用し有効なて利用シーン・サービスを考えたい
###おまけ（出力例）
```
$ python3 wc_input_original.py -d /usr/lib/aarch64-linux-gnu/mecab/dic/mecab-ipadic-neologd -s japanese.txt
```

```Japanese.txt
['あそこ', 'あたり', 'あちら', 'あっち', 'あと', 'あな', 'あなた', 'あれ', 'いくつ', 'いつ', 'いま', 'いや', 'いろいろ', 'うち', 'おおまか', 'おまえ', 'おれ', 'お', 'がい', 'かく', 'かたち', 'かな', 'かやの', 'から', 'がら', 'きた', 'くせ', 'ここ', 'こっち', 'こと', 'ごと', 'こちら', 'ごっちゃ', 'これ', 'これら', 'ごろ', 'さまざま', 'さらい', 'さん', 'しかた', 'しよう', 'すか', 'ずつ', 'すね', 'すべて', 'ぜんぶ', 'そう', 'そこ', 'そちら', 'そっち', 'そで', 'それ', 'それぞれ', 'それなり', 'たくさん', 'たち', 'たび', 'ため', 'だめ', 'ちゃ', 'ちゃん', 'てん', 'とおり', 'とき', 'どこ', 'どこか', 'ところ', 'どちら', 'どっか', 'どっち', 'どれ', 'なか', 'なかば', 'なく', 'なに', 'など', 'なん', 'なんか', 'に', 'の', 'はじめ', 'はず', 'はるか', 'ひと', 'ひとつ', 'ふく', 'ぶり', 'べつ', 'へん', 'ぺん', 'ほう', 'ほか', 'まさ', 'まし', 'まとも', 'まま', 'みたい', 'みつ', 'みなさん', 'みんな', 'もと', 'もの', 'もん', 'やつ', 'よう', 'よそ', 'わけ', 'わたし', 'ハイ', '上', '中', '下', '字', '年', '月', '日', '時', '分', '秒', '週', '火', '水', '木', '金', '土', '国', '都', '道', '府', '県', '市', '区', '町', '村', '各', '第', '方', '何', '的', '度', '文', '者', '性', '体', '人', '他', '今', '部', '課', '係', '外', '類', '達', '気', '室', '口', '誰', '用', '界', '会', '首', '男', '女', '別', '話', '私', '屋', '店', '家', '場', '等', '見', '際', '観', '段', '略', '例', '系', '論', '形', '間', '地', '員', '線', '点', '書', '品', '力', '法', '感', '作', '元', '手', '数', '彼', '彼女', '子', '内', '楽', '喜', '怒', '哀', '輪', '頃', '化', '境', '俺', '奴', '高', '校', '婦', '伸', '紀', '誌', 'レ', '行', '列', '事', '士', '台', '集', '様', '所', '歴', '器', '名', '情', '連', '毎', '式', '簿', '回', '匹', '個', '席', '束', '歳', '目', '通', '面', '円', '玉', '枚', '前', '後', '左', '右', '次', '先', '春', '夏', '秋', '冬', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '百', '千', '万', '億', '兆', '下記', '上記', '時間', '今回', '前回', '場合', '一つ', '年生', '自分', 'ヶ所', 'ヵ所', 'カ所', '箇所', 'ヶ月', 'ヵ月', 'カ月', '箇月', '名前', '本当', '確か', '時点', '全部', '関係', '近く', '方法', '我々', '違い', '多く', '扱い', '新た', 'その後', '半ば', '結局', '様々', '以前', '以後', '以降', '未満', '以上', '以下', '幾つ', '毎日', '自体', '向こう', '何人', '手段', '同じ', '感じ']
```
```入力.
> 名城大（名古屋市）は25日、リチウムイオン電池の開発でノーベル化学賞を受賞した同大学教授で旭化成名誉フェローの吉野彰さん（72）に「特別栄誉教授」の称号を授与した。吉野さんは2017年から、大学院理工学研究科の教授を務めており、週1回の講義を受け持っている。名城大によると、特別栄誉教授はノーベル賞を受賞した教員などをたたえるための称号。14年に終身教授の赤崎勇さんと元教授の天野浩さんが、青色発光ダイオード（LED）の開発でノーベル物理学賞を受賞したことをきっかけに創設した。 
```

```py
    splitted = " ".join([x.split("\t")[0] for x in t.parse(line).splitlines()[:-1] if x.split("\t")[1].split(",")[0] not in [""]])
    print("0",splitted)
    get_wordcrowd_color_mask(0,splitted, '')
    get_wordcrowd_color_mask(1,splitted, './mask_images/alice_color.png') 
```
```出力０.
0 名城大 （ 名古屋市 ） は 25日 、 リチウムイオン電池 の 開発 で ノーベル化学賞 を 受賞 し た 同 大学 教授 で 旭化成 名誉フェロー の 吉野彰 さん （ 72 ） に 「 特別栄誉教授 」 の 称号 を 授与 し た 。 吉野 さん は 2017年 から 、 大学院 理工学研究科 の 教授 を 務め て おり 、 週 1回 の 講義 を 受け持っ て いる 。 名城大 に よる と 、 特別栄誉教授 は ノーベル賞 を 受賞 し た 教員 など を たたえる ため の 称号 。 14年 に 終身 教授 の 赤崎勇 さん と 元 教授 の 天野浩 さん が 、 青色発光ダイオード （ LED ） の 開発 で ノーベル物理学賞 を 受賞 し た こと を きっかけ に 創設 し た 。
```
![0-yosino_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/b85337ed-1521-66f4-1528-f8f1fde670ee.png)
![1-yosino_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/0c936291-c6de-93db-6231-775e9a7b00af.png)

```py
    splitted = " ".join([x.split("\t")[0] for x in t.parse(line).splitlines()[:-1] if x.split("\t")[1].split(",")[0] not in ["助詞", "助動詞", "副詞", "連体詞","接続詞","動詞","記号"]])
    print("1",splitted)
    get_wordcrowd_color_mask(2,splitted, '')
    get_wordcrowd_color_mask(3,splitted, './mask_images/alice_color.png') 
```
![2-yosino_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/edb37e3d-cef9-c1bd-bd7c-92ba1540c1a2.png)
![3-yosino_名.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/7f1a5a6d-1251-f320-94bb-f85c4620c00d.png)

```出力１.
1 名城大 名古屋市 25日 リチウムイオン電池 開発 ノーベル化学賞 受賞 同 大学 教授 旭化成 名誉フェロー 吉野彰 さん 72 特別栄誉教授 称号 授与 吉野 さん 2017年 大学院 理工学研究科 教授 週 1回 講義 名城大 特別栄誉教授 ノーベル賞 受賞 教員 ため 称号 14年 終身 教授 赤崎勇 さん 元 教授 天野浩 さん 青色発光ダイオード LED 開発 ノーベル物理学賞 受賞 こと きっかけ 創設
```

```出力２.
2 名城大 名古屋市 25日 リチウムイオン電池 開発 ノーベル化学賞 受賞 同 大学 教授 旭化成 名誉フェロー 吉野彰 72 特別栄誉教授 称号 授与 吉野 2017年 大学院 理工学研究科 教授 1回 講義 名城大 特別栄誉教授 ノーベル賞 受賞 教員 称号 14年 終身 教授 赤崎勇 教授 天野浩 青色発光ダイオード led 開発 ノーベル物理学賞 受賞 きっかけ 創設
```
###おまけ（パラメター）
WordCloudの引数一覧（参考；以下のコード内説明より）
・[word_cloud/wordcloud/wordcloud.py](https://github.com/amueller/word_cloud/blob/master/wordcloud/wordcloud.py)

|     Parameters ||
|:--|:--|
|     font_path : string | Font path to the font that will be used (OTF or TTF).         Defaults to DroidSansMono path on a Linux machine. If you are on another OS or don't have this font, you need to adjust this path. |
|     width : int (default=400) |      Width of the canvas. |
|     height : int (default=200) |         Height of the canvas. |
|     prefer_horizontal : float (default=0.90) |      The ratio of times to try horizontal fitting as opposed to vertical.  If prefer_horizontal < 1, the algorithm will try rotating the word  if it doesn't fit. (There is currently no built-in way to get only vertical words.) |
|     mask : nd-array or None (default=None)| If not None, gives a binary mask on where to draw words. If mask is not  None, width and height will be ignored and the shape of mask will be used instead. All white (#FF or #FFFFFF) entries will be considerd "masked out" while other entries will be free to draw on. [This changed in the most recent version!] |
|     contour_width: float (default=0) |         If mask is not None and contour_width > 0, draw the mask contour. |
|     contour_color: color value (default="black")|  Mask contour color. |
|     scale : float (default=1) | Scaling between computation and drawing. For large word-cloud images, using scale instead of larger canvas size is significantly faster, but might lead to a coarser fit for the words. |
|     min_font_size : int (default=4) |Smallest font size to use. Will stop when there is no more room in this size.| 
|     font_step : int (default=1) | Step size for the font. font_step > 1 might speed up computation but give a worse fit. |
|     max_words : number (default=200) | The maximum number of words. |
|     stopwords : set of strings or None | The words that will be eliminated. If None, the build-in STOPWORDS list will be used. Ignored if using generate_from_frequencies. |
|     background_color : color value (default="black") |         Background color for the word cloud image. |
|     max_font_size : int or None (default=None) |  Maximum font size for the largest word. If None, height of the image is used. |
|     mode : string (default="RGB") |  Transparent background will be generated when mode is "RGBA" and background_color is None. |
|     relative_scaling : float (default='auto') | Importance of relative word frequencies for font-size.  With relative_scaling=0, only word-ranks are considered.  With relative_scaling=1, a word that is twice as frequent will have twice the size.  If you want to consider the word frequencies and not only        their rank, relative_scaling around .5 often looks good. If 'auto' it will be set to 0.5 unless repeat is true, in which  case it will be set to 0.  ..versionchanged: 2.0 Default is now 'auto'. |
|     color_func : callable, default=None |Callable with parameters word, font_size, position, orientation, font_path, random_state that returns a PIL color for each word. Overwrites "colormap". See colormap for specifying a matplotlib colormap instead. To create a word cloud with a single color, use          ``color_func=lambda *args, **kwargs: "white"``. The single color can also be specified using RGB code. For example ``color_func=lambda *args, **kwargs: (255,0,0)`` sets color to red. |
|     regexp : string or None (optional) | Regular expression to split the input text into tokens in process_text. If None is specified, ``r"\w[\w']+"`` is used. Ignored if using generate_from_frequencies. |
|     collocations : bool, default=True |  Whether to include collocations (bigrams) of two words. Ignored if using generate_from_frequencies. .. versionadded: 2.0 |
|     colormap : string or matplotlib colormap, default="viridis" |         Matplotlib colormap to randomly draw colors from for each word. Ignored if "color_func" is specified. .. versionadded: 2.0 |
|     normalize_plurals : bool, default=True    | Whether to remove trailing 's' from words. If True and a word appears with and without a trailing 's', the one with trailing 's' is removed and its counts are added to the version without        trailing 's' -- unless the word ends with 'ss'. Ignored if using generate_from_frequencies. |
|     repeat : bool, default=False | Whether to repeat words and phrases until max_words or min_font_size is reached. |
|     include_numbers : bool, default=False | Whether to include numbers as phrases or not. |
|     min_word_length : int, default=0 | Minimum number of letters a word must have to be included. |