毎日テキストマイニング

180日間、毎日テキストマイニングをするブログです

2018/8/12【50日目】ランダムフォレストで再び福岡聖菜のtweetを当てていく

単語の処理の仕方もかなりわかりましたので、今度こそ福岡聖菜tweetを判定したいと思います。 前回のダメな例がこちらです。

dailytextmining.hatenablog.com

前処理

6月10日から8月10日までの全tweetを取得します。列はid, name, tweetの3列、行数は3264行です。2ヶ月も続けていますと結構集まりますね。今日で50日目です。飽きずによくやっているなと思います。

とりあえず、pandasを使ってデータフレームとして読み込みます。

import pandas as pd
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt

train = pd.read_csv('all_20180812.csv',
                    header = 0,
                    delimiter = ",")

チュートリアルだとquoting=3という引数がありましたが、これをつけるとエラーが起こりましたので外しました。

tweet内容が読み込めたところで、nameを福岡聖菜の場合は1を、福岡聖菜ではない場合は0を代入にして正解ラベルを作ります。

train["name"].loc[train["name"] != "seina_fuku48"] = 0
train["name"].loc[train["name"] == "seina_fuku48"] = 1

それで、前回は気づかなかったのですが、ただ単に0と1を代入しただけでは、この0と1は文字列なんですよね。

print(train.dtypes)
id           int64
name        object
tweet       object
dtype: object

正解データとして扱うにはintにする必要がありますので、name内の数値をコピーして、新しくintの列を作ります。

train['name_int'] = train['name'].astype(np.int64)
train

実行結果はこんな感じです。

 id  name    tweet   name_int
0   1   0   #アイドル修業中♡公演\n来てくれて、見てくれて、\nありがとうございました???!\n\n...   0
1   2   0   今日は #山根涼羽 ずんちゃんと、\nカフェに行ったりお買い物しました☕??\n大切な同期と... 0
2   3   0   投票した\nマイスイートピアノちゃん\n17位だ???!!!!!\nピアノちゃんすき?!!!...   0

次に、tweet内容を詳しく見てみます。

train['tweet'][0]
'#アイドル修業中♡公演\n来てくれて、見てくれて、\nありがとうございました???!\n\nドラフト3期生の\n#齋藤陽菜 ちゃん(はーたん)(1枚目)と\n#石綿星南 ちゃん(せなたん)(2枚目)が劇場フル出演デビューでした~!✨\n出てくれ… https://t.co/PuFkSwSEnZ'

HTMLタグはないですね。なので、MeCabだけを関数としてまとめます。tweet内のリンク(http以降)を消すか消さないかで迷いましたが、リンクのあるなしも重要な要素だと思いますので、残しておきました。

import MeCab
import re

tagger = MeCab.Tagger ("-Owakati")

def tweet_to_words(raw_tweet):
    # letters_only = re.sub("(https?)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)", " ", tweet_text) 
    mecab = tagger.parse(raw_tweet)
    return(mecab)

結果はこんな感じです。

tweet_to_words(train["tweet"][0])
'# アイドル修業中 ♡ 公演 来 て くれ て 、 見 て くれ て 、 ありがとう ござい まし た ???! ドラフト 3 期生 の # 齋藤 陽 菜 ちゃん ( は ー たん )( 1 枚 目 ) と # 石綿 星 南 ちゃん ( せ な たん )( 2 枚 目 ) が 劇場 フル 出演 デビュー でし た ~!✨ 出 て くれ … https :// t . co / PuFkSwSEnZ \n'

ややばらけ過ぎている気がしますが、そのまま続けてみます。

この関数をtweetの数だけfor文で回していきます。

num_tweets = train["tweet"].size
clean_train_reviews = []

for i in range(0, num_tweets):
    clean_train_tweets.append( tweet_to_words( train["tweet"][i] ) )

print(clean_train_reviews)
TypeError: object of type 'float' has no len()

コードは間違っていないはずですが、エラーが起きました。 こういう場合はデータが悪いはずなので、tweet内容にNaNがないか見てみます。

train.isnull().any(axis=0)
id       False
name     False
tweet     True
name_int     False
dtype: bool

まぁ、NaNが当然ありますね。 いつも通り、train[train.isnull().any(1)]で検索していってNaNを消していきます。

NaNを全部消してみたところで、再実行。BeautifulSoupの警告が出てきましたが、実行できました。

from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(analyzer = "word",
                             tokenizer = None,
                             preprocessor = None,
                             stop_words = None,
                             max_features = 4000) 
train_data_features = vectorizer.fit_transform(clean_train_tweets)
train_data_features = train_data_features.toarray()
print(train_data_features.shape)
(2000, 4000)

2000行で最大に設定した4000個の特徴量ができました。 これをランダムフォレストに学習させます。正解ラベルはtrain["name_int”]です。

from sklearn.ensemble import RandomForestClassifier

forest = RandomForestClassifier(n_estimators = 100) 
forest = forest.fit(train_data_features, train["name_int"])

何も起こらないので学習できたようです。 学習ができたところで、8月10日から12日の2日分の最新tweetを投げてみましょう(185件)。中身はidとtweetだけで、nameは外しています。

test = pd.read_csv("all_20180810_12.csv", header=0, delimiter=",")
test
 id  tweet
0   9876    握手会ありがとうございました??\n\nじゃんけん大会本戦出場です✨✌?️\nLovelyS...
1   9877    本線いっくよぉ〜✨✨\n\nSugarが勝ってくれたのぉ!???\n\nLovelyとSug...

あとは同じように、特徴量を出して、forest.predict(test_data_features)とpredictするだけです。

num_reviews = len(test["tweet"])
clean_test_reviews = [] 

for i in range(0,num_reviews):
    clean_review = tweet_to_words( test["tweet"][i] )
    clean_test_reviews.append( clean_review )

test_data_features = vectorizer.transform(clean_test_reviews)
test_data_features = test_data_features.toarray()

result = forest.predict(test_data_features)

output = pd.DataFrame( data={"id":test["id"], "name":result} )
output.to_csv( "fukuoka.csv", index=False)

このようなcsvができました。

id,name
9876,0
9877,0
9878,0
9879,0
9882,0
9883,0
9884,0
9888,0
9889,0
9890,0
9891,0
9892,0
9894,0
9900,0
9901,0
(以下、略)

これをずっと見ていくと、1つだけ1(正解)があります。その中身を見てみます。

握手会ありがとうございました。 、、、、、元気です? #きょうのせいちゃん https://t.co/Z68vsoSjxs

きた、福岡聖菜のtweeetです。きょうのせいちゃん というのがありますので、わかりやすかったのかもしれませんね。下記の2つは0(福岡聖菜ではない)と判別されていました。

きぃちゃんお誕生日おめでとう✨
AKB最年少と言ってたのが
遥か彼方の18歳にお互いなったね笑
長女らしい優しさとまだまだ子供な部分
1つ1つが愛おしいです。
ステージのキラキラアイドル、
バラエティの天然のおもしろさ
きぃちゃん… https://t.co/Y3dp0vfFHZ

"まけたぁぁぁぁ???
#AKBじゃんけん https://t.co/pnzYUkU0vP"

2番目のはどうやっても判定できなさそうですが、色々パラメータをいじったらもしかしたら正解できるかもしれません。ちょっと楽しみです。

今日の結果

今日のAKBメンバーによる呟きは95件でした。 3日間続いた握手会は今日で最後だったようです。 f:id:rimt:20180813012900p:plain

'嬉しい': 11, '楽しい': 8, '可愛い': 8, '珍しい': 5, '正しい': 4, 'っぽい': 4, 'よろしい': 3, '弱い': 3, 'いい': 3, 'ない': 2, '無い': 2, '早い': 2, '五月蝿い': 2, 'よい': 2, '良い': 1, '遠い': 1, '辛い': 1, '眩しい': 1, '切ない': 1, '美しい': 1, 'うれしい': 1, '悔しい': 1, '優しい': 1, 'おしい': 1, 'おもしろい': 1, 'つらい': 1, 'もどかしい': 1})
'アクシュカイ': 57, '部': 40, '今日': 36, '会': 25, '浴衣': 24, '写真': 24, '大会': 23, '日': 19, '明日': 16, '本戦': 15, 'ちゃん': 15, 'レーン': 15, 'ん': 14, '応援': 13, '皆さん': 12, '嬉しい': 11, 'さん': 11, '当日': 11, '券': 11, '目': 11, '人': 11, 'お願い': 10, '笑': 10, '動画': 10, 'ステージ': 10, 'こと': 9, 
'アクシュカイ': 57, 'する': 51, '部': 40, 'ける': 40, '今日': 36, '会': 25, '浴衣': 24, '写真': 24, '大会': 23, '日': 19, '来る': 17, '明日': 16, 'くださる': 16, '本戦': 15, 'ちゃん': 15, 'レーン': 15, 'ん': 14,