乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

形態素解析

今回はあの
丹生ちゃんのタルタルチキンしりとりを
再現するプログラムを考えてみました。

解説動画はこちら



知らない人のために補足しておくと
日向坂46の丹生あかりちゃんには
なんかしらの言葉をしりとりで
タルタルチキンに繋げる・・・
という特技があります。

それをプログラムで再現します。

さて、今回のテーマはしりとりです。
しりとりをプログラムで行うには
単語のデータがないとうまくはいきません。

そこでMeacbの辞書を使って
しりとりを行えるような
プログラムを作っていきたいと思います。

Google Colab上で動くようなコードになってます。

まず最初はMecabをインストールします。
Mecabは形態素解析用のライブラリで
これには辞書が含まれています。

その辞書を使っていくわけですね。
GoogleColabにMecabをインストールするのは
次のコードです。
# MeCab と 辞書(mecab-ipadic-NEologd)のインストール 
!apt-get -q -y install sudo file mecab libmecab-dev mecab-ipadic-utf8 git curl python-mecab > /dev/null
!git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git > /dev/null 
!echo yes | mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n > /dev/null 2>&1
!pip install mecab-python3 > /dev/null
!ln -s /etc/mecabrc /usr/local/etc/mecabrc
!echo `mecab-config --dicdir`"/mecab-ipadic-neologd"

インストールできたかどうか
確かめてみましょう。

次のコードで実行確認ができます。
# テスト
import MeCab
import random

sample_txt = "鬼滅の刃の炭次郎"
path = "-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd"
m = MeCab.Tagger(path)
print("Mecab ipadic NEologd:\n",m.parse(sample_txt))
Mecab ipadic NEologd:
 鬼滅の刃 名詞,固有名詞,一般,*,*,*,鬼滅の刃,キメツノヤイバ,キメツノヤイバ
助詞,連体化,*,*,*,*,の,ノ,ノ
名詞,固有名詞,人名,姓,*,*,炭,タン,タン
次郎 名詞,固有名詞,人名,名,*,*,次郎,ジロウ,ジロー
EOS


いい感じで形態素解析出来ていますね。

形態素解析は文章を単語に分割しますが
辞書に無い固有名詞はより細かい単語に
分割されてしまいます。

この場合、「鬼滅の刃」がそれに当たります。
この辞書は「鬼滅の刃」が辞書登録されていますね。

この辞書データを使ってしりとり用の
辞書データを作っていきます。
# ここのパスは要チェック
dic_path = '/content/mecab-ipadic-neologd/build/mecab-ipadic-2.7.0-20070801-neologd-20200910/mecab-user-dict-seed.20200910.csv'
dic_data = {}
with open(dic_path) as _f:
    for i,row in enumerate(_f):
        rows = row.replace('\n','').split(',')
        word = rows[0]
        yomi = rows[11]
        hinsi = rows[4]
        
        # 品詞の分類
        if '記号' in m.parse(word[0]):
          continue
        # 名詞かつ ンやーでない 数字でもないものだけ登録
        if '名詞' in hinsi and len(yomi)>2 and 'ン'!=yomi[-1] and 'ー'!=yomi[-1] and not any([a.isnumeric() for a in word]):
          #print(rows)          
          s_word = yomi[0]
          e_word = yomi[-1]
          if s_word in dic_data:
            tmp_s = dic_data[s_word]
            if e_word in tmp_s:
              tmp_e = tmp_s[e_word]
              tmp_e.append({'word':word,'yomi':yomi})
            else:
              tmp_e = [{'word':word,'yomi':yomi}]
            tmp_s[e_word] = tmp_e
            dic_data[s_word] = tmp_s
          else:
            tmp_e = [{'word':word,'yomi':yomi}]
            dic_data[s_word] = {s_word:tmp_e}
これで辞書データが出来ました。
辞書のパスは変わる可能性があるので
うまく合わせてください。

次にしりとりを行う関数の定義です。

最初の単語を入力して
次の単語をしりとりとして
繋がるようなモノを選んで
最後の言葉につなげるようにします。
# 最初の終わりと最後のワードの始まりの文字
def word_b_and_e(target_word,last_word):
  targets = m.parse(target_word)
  targets = targets.replace('\nEOS\n','').split('\n')
  target_e = targets[-1].split(',')[-2][-1]
  lasts = m.parse(last_word)
  lasts = lasts.replace('\nEOS\n','').split('\n')
  lasts_s = lasts[0].split(',')[-2][0] if lasts[0].split(',')[-2]!='*' else lasts[0].replace('\t',',').split(',')[0][0]
  return target_e,lasts_s

# 次のワードを選択する
def nect_select(target_e):
  next_dic = dic_data[target_e]
  next_e_word = random.choice(list(next_dic.keys()))
  next_ends = next_dic[next_e_word]
  next_word_dic = random.choice(next_ends)
  return next_e_word,next_word_dic

# しりとり5
def siritori5(target_word,last_word):
  for a in range(10):
    try:
      words_route = [target_word]
      target_e , lasts_s = word_b_and_e(target_word,last_word)
      next_target = target_e
      for i in range(2):
        next_e_word , next_word_dic = nect_select(next_target)
        words_route.append('{0}({1})'.format(next_word_dic['word'],next_word_dic['yomi']))
        next_target = next_e_word

      lasts = random.choice(dic_data[next_target][lasts_s])
      words_route.append('{0}({1})'.format(lasts['word'],lasts['yomi']))
      words_route.append(last_word)
      for w in words_route:
        print(w)
      break
    except:
      pass

これでしりとりを行う準備が出来ました。

あとは実行です。
最初の単語と
最後につなげる単語を用意しておきます。


今回は始まりを「鬼滅の刃」
終わりを「タルタルチキン」にします。

結果はランダムで選ばれます。
# 実行
target_word = '鬼滅の刃'
last_word = 'タルタルチキン'
siritori5(target_word,last_word)
鬼滅の刃
バイエルン国立バレエ(バイエルンコクリツバレエ)
エドゥアードヴェジンヌ(エドゥアードヴェジンヌ)
沼尻健太(ヌマジリケンタ)
タルタルチキン

鬼滅の刃
馬簾水母(バレンクラゲ)
ゲッコウガ(ゲッコウガ)
ガジンフジタ(ガジンフジタ)
タルタルチキン

鬼滅の刃
vanellope(バネロペ)
ペドロバルボーザ(ペドロバルボーザ)
ザカリウス親方(ザカリウスオヤカタ)
タルタルチキン


・・・・

なんか聞いたことの無い単語ばかり

たまにまともな奴がでやりしますんで
試したい方は
何回もやり直してみて下さい。

今回は丹生ちゃんの
タルタルチキンしりとりを再現する
プログラムでした。

いやータルタルチキン
最高ですねーーー

今日はここまでです
それでは。


今回はクリエイティブ力を高めるために
シルエット画像でワードクラウドを
作ってみたいと思います 

解説動画はこちら


ワードクラウドについては過去に
こちらの記事でも行っていますので
気になる方は見てみてください

ワードクラウドが面白い


さて
今回はGoogle Colab上で実行できるコードにしていますので
試したい方はうまくコピペして実行してみてください。

最初に用意するものとして
・ワードクラウド用のテキスト
(単語 単語 ・・・ のように単語と単語の間を
スペースで分けた文章ファイル)
・マスクに使用する画像
(グレースケール,アルファなしPNG)

この二つが必要です

ワードクラウドのサンプルを通して
やり方を見ていきましょう。

ワードクラウドのサンプル


アリスのサンプルを実行してみたいと思います

マスク画像を入手するには次のコードを実行してください
!wget https://raw.githubusercontent.com/amueller/word_cloud/master/examples/alice_mask.png

次にテキストファイルの入手は次のコードを実行してください
# アリスのテキストを入手
import requests
from bs4 import BeautifulSoup
url = 'https://github.com/amueller/word_cloud/blob/master/examples/alice.txt'
res = requests.get(url)
soup = BeautifulSoup(res.content,'lxml')
texts = soup.find('table').text
file_name = 'alice.txt'
with open(file_name,'w') as file:
  file.write(texts)

これでテキストとマスク画像が
Colabのファイル置き場に配置されたと思います

ここからはワードクラウドを作る
サンプルコードを実行してみましょう

最初はライブラリの読み込みからです
# ライブラリの読み込み
from os import path
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import os
from wordcloud import WordCloud, STOPWORDS

次にファイルの読み込みです
# テキストとマスク画像の読み込み
text = open('alice.txt').read()
mask = np.array(Image.open("alice_mask.png"))

読み込んだマスク画像をみてみましょう。
# マスク画像の表示
plt.imshow(mask, cmap=plt.cm.gray, interpolation='bilinear')
plt.axis("off")
plt.show()
alice_mask
このような画像ですね
これを使ってワードクラウドにしていきます

ここからはワードクラウドの設定を行うコードです
# 不要ワードの設定
stopwords = set(STOPWORDS)
stopwords.add("said")

# ワードクラウドの読み込み
wc = WordCloud(background_color="white", max_words=2000, mask=mask,
              stopwords=stopwords, contour_width=3, contour_color='steelblue')

除外ワードやマスク画像を引数で指定します

ワードクラウドの作成は次のコードで行う事ができます

# テキストをワードクラウド化
wc.generate(text)

# ファイル保存
wc.to_file("alice.png")
これでワードクラウドが出来たので見てみましょう
# show
plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.imshow(wc, interpolation='bilinear')
plt.axis("off")
plt.subplot(1,2,2)
plt.imshow(mask, cmap=plt.cm.gray, interpolation='bilinear')
plt.axis("off")
plt.show()
alice

不思議の国のアリスの画像に沿うように
単語が配置されます。

なかなか芸術的ですよね

次は日本語で行ってみましょう。
日本語で行うには少しコツが必要です

日本語に対応する場合は
・日本語のフォントのインストール
・Janomeライブラリのインストール
こちらが必要になります。


今回は日本語の文章として
青空文庫の「我輩は猫である」を
引用してみたいと思います


吾輩は猫であるを入手
# 吾輩は猫であるを入手
import requests
from bs4 import BeautifulSoup
url = 'https://www.aozora.gr.jp/cards/000148/files/789_14547.html'
res = requests.get(url)
soup = BeautifulSoup(res.content,'lxml')
texts = soup.find('div',class_='main_text').text
file_name = 'wagahaihaneko.txt'
with open(file_name,'w') as file:
  file.write(texts)
このコードを実行してもらうと
文章ファイルが手に入ると思います

次にこの文章を分かち書きしないと
日本語のワードクラウドに出来ないので
分かち書き用のライブラリである
「janome」をインストールします

Janomeのインストール
pip install janome

GoogleColabは日本語に対応していないので
日本語で表示させるには日本語フォントは必要です

次のコードを実行してインストールしましょう

日本語フォントのインストール
# 「IPA」フォントをインストール
!apt-get -y install fonts-ipafont-gothic
# matplotlibのキャッシュをクリア
!rm /root/.cache/matplotlib/fontlist-v300.json
日本語がうまく出ない場合はここで
「ランタイムを再起動」を 行ってから
以下を実行してみましょう


日本語でのワードクラウド作成
# ライブラリの読み込み
from os import path
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import os
from wordcloud import WordCloud, STOPWORDS
from janome.tokenizer import Tokenizer

分かち書きをするコードです
# Janomeの呼び出し
t = Tokenizer()
s = open('wagahaihaneko.txt').read()

# 文章を分かち書きして名詞だけ抜き出す
wakati_text = [token.surface for token in t.tokenize(s) if token.part_of_speech.startswith('名詞')]

# 名詞をひとまとめにする
wakati = " ".join(map(str, wakati_text))

# マスク画像の読み込み
mask = np.array(Image.open("alice_mask.png"))

これで分かち書きされた文章データになりました。
ワードクラウドを作ってみましょう。
引数には日本語フォントのパスの指定が追加になっています。

# 日本語フォントの指定
font_path = '/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf'
# ワードクラウドの読み込み
wc = WordCloud(background_color="white", max_words=2000, mask=mask,
              contour_width=3, contour_color='steelblue', font_path=font_path)

# テキストをワードクラウド化
wc.generate(wakati)

# ファイル保存
wc.to_file("waganeko.png")

# show
plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.imshow(wc, interpolation='bilinear')
plt.axis("off")
plt.subplot(1,2,2)
plt.imshow(mask, cmap=plt.cm.gray, interpolation='bilinear')
plt.axis("off")
plt.show()
waganeko


せっかくなので
サンプル画像ではなく猫の画像で試してみましょう。

何かしらのグレースケールの猫画像をご用意ください
それをColabのファイル置き場に置いて実行すると

# マスク画像の読み込み
mask = np.array(Image.open("neko.png"))

# 日本語フォントの指定
font_path = '/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf'
# ワードクラウドの読み込み
wc = WordCloud(background_color="white", max_words=200, mask=mask,
              contour_width=3, contour_color='steelblue', font_path=font_path)

# テキストをワードクラウド化
wc.generate(wakati)

# ファイル保存
wc.to_file("neko_result.png")

# show
plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.imshow(wc, interpolation='bilinear')
plt.axis("off")
plt.subplot(1,2,2)
plt.imshow(mask, cmap=plt.cm.gray, interpolation='bilinear')
plt.axis("off")
plt.show()
neko_result

こんな感じで猫の形にワードクラウドが作られます
なかなか面白いですよね!!!

まとめですが
ワードクラウドの作り方は
1.ワードクラウド用のテキストを用意する
2.マスク画像を用意する
3.日本語で行う場合は日本語フォントをインストールする
4.コードを実行する

こんな手順になります

画像をうまく活用すると
すごく芸術的な表現になるんじゃないかなーと
思ってみたり

気になる方は試してみてください
それでは



今回はとある映像コンテンツの
タイトルを調べてみました。

解説動画はこちら




ラグジュTVは凄まじく
素晴らしい映像コンテンツの
シリーズ物で1326本(動画作成時)
ありました。

さて調査方法ですが
次のような手順で行っています。

1.タイトルをスクレイピング
2.データ化
3.前処理(辞書作成,不要文字削除)
4.形態素解析
5.キーワードカウント

Pythonではスクレイピングや
形態素解析を簡単に行うことができます。

とあるサイトをスクレイピングするコードは
お伝えできないのですが、
形態素解析は janome ライブラリを用いて
タイトルをキーワードに分割し
その出現頻度でランク付けしました。


TOP41-50
41自ら
42スタイル
43彼氏
44よう
45男性
46全身
47濃厚
48敏感
49スレンダー
50興奮

TOP31-40
31カラダ
32美女
33
34旦那
35男根
36ランジェリー
37卑猥
38責め
39本能
40彼女



TOP21-30
21恍惚
22グラマラスボディ
23AV
24
25オイル
26必見
27性欲
28何度
29ボディ
30

口に出すのも恥ずかしい
言葉がたくさん並んでいますねーーーー

TOP20からは動画の方をご覧くださいませ

それでは。
 

TKO木下の動画が絶賛炎上中だと聞きつけまして
早速コメントを感情分析してみました

解説動画はこちら


炎上している動画はこんな感じです。

動画タイトル:TKO木下よりご報告がございます。
公開日:2020/04/01
2020/04/11 16:00現在
視聴回数:2,845,395 回視聴
高評価:4771件
低評価:28万件
コメント数:50,898 件

素晴らしい低評価の数ですねー。
私は動画は見ておりませんが
低定評に1評投じさせていただきました。


さて
感情分析のやり方ですが
Youtubeのコメントと形態素解析
それと単語感情極性対応表というものを使います。

単語感情極性対応表は
こちらを参照してください。



中身はこんな感じで

ない:ない:助動詞:-0.999997
酷い:ひどい:形容詞:-0.999997
病気:びょうき:名詞:-0.999998
死ぬ:しぬ:動詞:-0.999999
悪い:わるい:形容詞:-1

ポジティブワード、ネガティブワードがあり
ポジティブ方向になると0から1点
ネガティブ方向は-1から0点の間の数値を取ります。

単語のポジネガ具合が分かる訳です。

手順としては

1.事前にYoutubeのコメントを集める。
  Youtubeの動画のページをスクロールして
  コメント表示させてから保存する。

 2.BeautifulSoupでHTMLの中から
  コメントの部分だけ拾い上げる。

3.コメントを形態素解析して単語感情極性対応表と
  比較してポジネガ点数をつける
  ポジティブだとプラス,ネガティブだとマイナスの点になる

まずはじめに
動画のコメントを集めなければならないので
動画のページをブラウザーで開いて
コメントを表示させたら、HTMLファイルに保存します。

次にPythonのコードを使って
HTMLの構文解析を行い、コメントを抽出します。

まず必要なライブラリは
from bs4 import BeautifulSoup
from janome.tokenizer import Tokenizer
t = Tokenizer()

janomeは後に使いますが、インストールしていない方は
pip などでインストールしておきましょう。

次にhtmlファイルを読み込んで変数に格納しておきます。
with open('htmlのファイル') as _f:
    html = _f.read()


お次はコメント部分の抽出です。
htmlファイルの中からコメントを抽出して
リストに格納しています。
soup = BeautifulSoup(html ,'lxml')
contents = soup.find('div',id='contents')
ytds = contents.find_all('ytd-comment-thread-renderer')
result_list = []
for ytd in ytds:
    span1 = ytd.find('span',class_="style-scope ytd-comment-renderer")
    name = span1.text.replace('\n','').replace(' ','') if span1 is not None else ''
    span2 = ytd.find('span',id="vote-count-middle")
    high = span2.text.replace('\n','').replace(' ','') if span2 is not None else ''
    come1 = ytd.find('yt-formatted-string', id="text")
    rep = come1.text.replace(' 件の返信を表示','').replace(' ','') if come1 is not None else ''
    come2 = ytd.find('div',id="content", class_="style-scope ytd-expander")
    com = come2.text.replace('\n','').replace(' ','') if come2 is not None else ''
    tmp = [name,high,rep,com]
    result_list.append(tmp)

次は形態素解析です。

単語感情極性対応表は事前にダウンロードしておいて
CSVファイルなどにしておいてください。

単語を辞書型に取り込みます。
posinega = {}
with open('posinega.csv') as _f:
    for row in _f:
        rows = row.replace('\n','').split(':')
        posinega[rows[0]] = float(rows[3])

最後にコメントを形態素解析して
ポジネガで点数をつけていきます。
nega_result = {}
for rows in result_list:
    count = 0
    for token in t.tokenize(rows[3]):
        h = token.part_of_speech.split(',')[0]
        if any(['名詞'==h,'形容詞'==h,'動詞'==h]):
            num=len(token.surface)
            key = token.surface
            if key in posinega:
                count+=posinega[key]
    nega_result[rows[3]]=count

これでコメントにポジネガの点数が付きました。

最もネガティブなコメントは・・・

-53.76871590000002
私は叙々苑でバイトをしていた者です。
1年前、TKO木下が私のバイト先東京の叙々苑を
訪れた際の記憶は忘れられません。 悪い意味で。
TKO木下は入店早々に既に様子がおかしく、
ホール担当だった私の胸ぐらを急に掴み一言。
「さっさと案内しろやボケ」と。
しかし木下が入店する所を見て3秒程で
駆けつけたのにも関わらすこの暴言暴力
・・・
辛いです。

と全文はかなり長いので端折りますが
冒頭を見るだけでもかなりネガティブさが伝わる文面です。

ネガティブの一番悪いものでも-1点なので
60個近くネガティブな単語が使われているコメントでした。

これだけの長文を想像で書くのは無理でしょうから
実体験だったんだろうと思いますね。

というようなネガティブコメントが多数並んでいました。

逆に
ポジティブなコメント有るんかな?
みてみましょう。

11.14069 ウケる笑笑低評価笑笑笑笑笑笑笑笑笑笑
2.890590 おはよう。くっさいなぁ~笑笑笑
2.530665 祝!低評価日本一更新!
1.666281 2倍速だと面白い
1.412733 朗報低評価日本一達成
1.374283 むしろコメ欄見にきてる笑日本一の低評価王にしてあげるためにみんな低評価押そうな
1.299542 宮迫博之の真似すれば売れるとおもったんやろ笑笑
1.261103 (*'∇')/゚・:*【祝】*:・゚\('∇'*)低評価日本一!よっ!!あっぱれ!流石です!!
0.989242 ヒカルを越した天才
0.989199 適当にやってもいいよ君が面白いと思っても多分面白くないから
0.989199 ff7本当に面白いよ😃
0.963530 なんかこんだけ荒れてることすらおいしいな〜とか思ってそう。笑 0.935754 大喜利コメ見てるから黙れ
0.932040 バチスタすごい好きでしたw
0.932040 本当にお笑いが好きならやらんやろアホか
0.921042 うちの姪が初めて掴まり立ちしましてね。みんなで喜びました。
0.769555 来世で頑張って下さい❗️来世で応援します‼️
0.650668 ファンのみなさま、、、っておるんか?(笑)
0.568479 低評価日本一おめでとう㊗️あと、誰に謝ってんねん
0.568479 おめでとう低評価日本一‼️
0.568479 低評価日本一おめでとうございます!
0.568479 低評価日本一おめでとナス!


低評価おめでとう、など
悪い意味で褒めてる感じですねwwwwww。

というわけで、まとめですが

日頃の行いは大事です。
見てない所でも敬意を払うことは当たり前だと思います。
特にタレントさんの私生活には
その人となりが出るようですね。

また素行の悪い方がテレビに出るのは
あまり良いことだとは思いません。

ネガティブなイメージの有るタレントをCMなどに使うと
商品、サービスにもネガティブイメージがつくので
我々企業側の人間はこういうタレントは
使わないようにしないといけないです。

ということで動画コメントの
感情分析でした。

皆様も
日頃の行いには気をつけたほうが良いですよ
誰がみてるか分かりませんし。

それでは


このページのトップヘ