Twitterアナリティクスのデータで形態素分析してコサイン類似度で類似ツイート検索してWordCloudを表示するまで
Twitterアナリティクスでデータ取得
まずTwitterアナリティクスからツイートのデータをby tweetで取得します。
下の画像の①〜③の手順でデータをダウンロードしてください。
データ整理
Jupyter LabやNotebookで、以下のコードを実行してファイルをまとめます。
import os
import glob
import pandas as pd
path = "./data_by_tweet"
files = glob.glob(path + '/tweet_activity*')
for f in files:
os.rename(f, path + "/tw" + f[-25:-18] + ".csv")
files = glob.glob(path + '/tw_*')
tw = pd.DataFrame()
for f in files:
tw = pd.concat([tw,pd.read_csv(f)])
次に、データクリーニングをしていきます。
tw['時間'] = pd.to_datetime(tw['時間'])
tw = tw.sort_values('時間', ascending=False)
tw = tw.reset_index(drop=True)
pd.set_option('display.max_columns', tw.shape[1])
print(tw.shape)
(1402, 40)
欠損値を確認します。
tw.isnull().sum()
URLクリック数 0 いいね 0 アプリインストール 701 アプリ表示 701 インプレッション 0 エンゲージメント 0 エンゲージメント率 0 ダイアル式電話 701 ツイートID 0 ツイートの固定リンク 0 ツイートをメール送信 701 ツイート本文 0 ハッシュタグクリック 0 フォローしている 0 プロモのURLクリック数 701 プロモのいいね 701 プロモのアプリインストール 701 プロモのアプリ表示 701 プロモのインプレッション 701 プロモのエンゲージメント 701 プロモのエンゲージメント率 701 プロモのダイアル式電話 701 プロモのツイートをメール送信 701 プロモのハッシュタグクリック 701 プロモのフォローしている 701 プロモのメディアのエンゲージメント数 701 プロモのメディアの再生数 701 プロモのユーザープロフィールクリック 701 プロモのリツイート 701 プロモの固定リンクのクリック数 701 プロモの詳細クリック 701 プロモの返信 701 メディアのエンゲージメント数 0 メディアの再生数 0 ユーザープロフィールクリック 0 リツイート 0 固定リンクのクリック数 701 時間 0 詳細クリック 0 返信 0 dtype: int64
無意味なデータを削除していきます。
tw = tw.replace({'-': pd.np.nan,'NaN': pd.np.nan, 'nan': pd.np.nan})
tw = tw.dropna(how='all',axis=1)
print(tw.shape)
(1402, 22)
# 重複しているツイートをツイートIDについて一意に変更
tw = tw.loc[~(tw.duplicated(subset=['ツイートID']))]
tw = tw.reset_index(drop=True)
tw = tw.fillna(0)
# 全部0の列を削除
all_0_cols = []
for col in tw.columns:
if (tw[col] == 0).all() == True:
all_0_cols.append(col)
print(all_0_cols)
tw = tw.drop(columns=all_0_cols)
tw = tw.sort_values('時間', ascending=False)
tw = tw.reset_index(drop=True)
print(tw.shape)
['アプリインストール', 'アプリ表示', 'ダイアル式電話', 'ツイートをメール送信', '固定リンクのクリック数']
(701, 17)
もう一度欠損値を確認します。
tw.isnull().sum()
URLクリック数 0 いいね 0 インプレッション 0 エンゲージメント 0 エンゲージメント率 0 ツイートID 0 ツイートの固定リンク 0 ツイート本文 0 ハッシュタグクリック 0 フォローしている 0 メディアのエンゲージメント数 0 メディアの再生数 0 ユーザープロフィールクリック 0 リツイート 0 時間 0 詳細クリック 0 返信 0 dtype: int64
良い感じなので出力します。
tw.to_csv('data_by_tweet/tw_cleaned.csv',index=False)
これである程度データをクリーニングして出力できました。
データクレンジング・可視化
ライブラリをインポートします。
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.family'] = 'IPAexGothic'
import seaborn as sns
sns.set()
先ほど出力したデータをインポートします。
tw = pd.read_csv('./data_by_tweet/tw_cleaned.csv')
カラムを追加します。
# カラム追加
# 画像や動画を添付している場合はTrue、そうでなければFalse
tw['media_exist'] = tw['ツイート本文'].str.contains('t.co')
# リプライしたツイートであればTrue、そうでなければFalse
tw['reply_flg'] = tw['ツイート本文'].str.contains('@')
import re
def format_text(text): # ツイート本文から余計なものを削除する
text=re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text)
text=re.sub(r'[!-~]', "", text)#半角記号,数字,英字
text=re.sub(r'[︰-@]', "", text)#全角記号
text=re.sub('\n', " ", text)#改行文字
return text
tw['ツイート本文_x'] = tw['ツイート本文'].apply(lambda s: format_text(s))
tw['文字数'] = tw['ツイート本文_x'].str.len()
tw['時間'] = pd.to_datetime(tw['時間'])
tw['MD'] = tw['時間'].dt.strftime('%m%d')
tw['MONTH'] = tw['時間'].dt.month
tw['DAY'] = tw['時間'].dt.day
tw['TIME'] = tw['時間'].dt.hour
tw['WEEKDAY'] = tw['時間'].dt.weekday
tw['WEEK'] = tw['時間'].dt.week
tw = tw.drop(columns='ツイートの固定リンク')
print(tw.shape)
(701, 26)
時間帯ごとのインプレッション、エンゲージメント、いいね、リツイート、の平均と、時間帯ごとの平均ツイート数を比較します
fig = plt.figure(figsize=(20,12))
fig.suptitle('mean', fontsize=24)
axes1 = fig.add_subplot(221)
axes2 = fig.add_subplot(222)
axes3 = fig.add_subplot(223)
axes4 = fig.add_subplot(224)
s = tw.groupby('TIME').size()
data1 = tw.groupby('TIME')['インプレッション'].mean()
data2 = tw.groupby('TIME')['エンゲージメント'].mean()
data3 = tw.groupby('TIME')['いいね'].mean()
data4 = tw.groupby('TIME')['リツイート'].mean()
axes1.plot(data1.index, data1.values, label=data1.name)
axes1_s = axes1.twinx()
axes1_s.bar(data1.index,s,alpha=0.3,label='ツイート数')
axes2.plot(data2.index, data2.values, label=data2.name)
axes2_s = axes2.twinx()
axes2_s.bar(data2.index,s,alpha=0.3,label='ツイート数')
axes3.plot(data3.index, data3.values, label=data3.name)
axes3_s = axes3.twinx()
axes3_s.bar(data3.index,s,alpha=0.3,label='ツイート数')
axes4.plot(data4.index, data4.values, label=data4.name)
axes4_s = axes4.twinx()
axes4_s.bar(data4.index,s,alpha=0.3,label='ツイート数')
axes1.set_title(data1.name)
axes2.set_title(data2.name)
axes3.set_title(data3.name)
axes4.set_title(data4.name)
axes1.set_xticks(data1.index)
axes2.set_xticks(data2.index)
axes3.set_xticks(data3.index)
axes4.set_xticks(data4.index)
axes1_s.legend(loc=0)
axes2_s.legend(loc=0)
axes3_s.legend(loc=0)
axes4_s.legend(loc=0)
axes1.legend()
axes2.legend()
axes3.legend()
axes4.legend()
print(np.corrcoef(s.values, data1.values))
print(np.corrcoef(s.values, data2.values))
print(np.corrcoef(s.values, data3.values))
print(np.corrcoef(s.values, data4.values))
[[ 1. -0.65646947] [-0.65646947 1. ]] [[ 1. -0.60037046] [-0.60037046 1. ]] [[ 1. -0.61819986] [-0.61819986 1. ]] [[ 1. -0.55087126] [-0.55087126 1. ]]
それぞれの項目の時間帯別中央値や平均が、時間帯別ツイート数と負の相関関係にあることがわかります。
形態素解析
形態素解析とは、文章を構成されている最初単位の単語に分けて、解析する方法です。今回は名詞のみでやっていきます。
その単語が出てきた「いいね」の平均と、その単語がでてきた回数を見れるようにしてみました。また、でてきた回数が3回より多いもののみに限定しています。
import MeCab
tagger = MeCab.Tagger('-d /usr/local/lib/mecab/dic/ipadic')
all_words = []
goods = []
parts = ['名詞']
for n in range(len(tw)):
text = tw.iloc[n]['ツイート本文_x']
words = tagger.parse(text).splitlines()
words_arr = []
for i in words:
if i == 'EOS' or i == '':continue
word_tmp = i.split()[0]
part = i.split()[1].split(',')[0]
if not (part in parts):continue
words_arr.append(word_tmp)
goods.append(tw.iloc[n]['いいね'])
all_words.extend(words_arr)
df = pd.DataFrame({
'word':all_words,
'いいね':goods,
'回数':len(all_words) * [1]
})
df_good = df.groupby('word')['いいね'].mean()
df_count = df.groupby('word')['回数'].sum()
df_a = pd.concat([df_good,df_count],axis=1)
df_rank = df_a.loc[df_a['回数']>3].sort_values('いいね', ascending=False).reset_index()
df_rank.head()
意外にもダッフィという言葉がフォロワーからいいねされているようです。これは、ペットとぬいぐるみの組み合わせがフォロワーの心をくすぐっていることを示唆しているのかもしれません。
とはいえ、他のぬいぐるみの情報が取れていないので、たまたま「ダッフィ」の記事がバズっただけの可能性が高そうです。
それでも他のぬいぐるみをツイートに登場させることで、ぬいぐるみの存在がいいね数と相関しているかを調べることは有益だと考えられます。
類似ツイート検索
類似のツイートを探す方法を実装していきます。まず名詞、動詞、形容詞で形態素解析します。
tagger = MeCab.Tagger('-d /usr/local/lib/mecab/dic/ipadic')
all_words_df = pd.DataFrame()
parts = ['名詞','動詞','形容詞']
goods = []
for n in range(len(tw)):
text = tw.iloc[n]['ツイート本文_x']
words = tagger.parse(text).splitlines()
words_df = pd.DataFrame()
for i in words:
if i == 'EOS' or i == '':continue
word_tmp = i.split()[0]
part = i.split()[1].split(',')[0]
if not (part in parts):continue
words_df[word_tmp] = [1]
goods.append(tw.iloc[n]['いいね'])
all_words_df = pd.concat([all_words_df, words_df],ignore_index=True)
all_words_df = all_words_df.fillna(0)
all_words_df
インプレッション、いいね、ユーザープロフィールクリックそれぞれが最高のツイートのインデックスを取得します。
print(tw.loc[tw['インプレッション'] == tw['インプレッション'].max()].index)
print(tw.loc[tw['いいね'] == tw['いいね'].max()].index)
print(tw.loc[tw['ユーザープロフィールクリック'] == tw['ユーザープロフィールクリック'].max()].index)
Int64Index([230], dtype='int64') Int64Index([234], dtype='int64') Int64Index([536], dtype='int64')
今回はインデックス230をえらびました。
コサイン類似度で探します。
tar_text = all_words_df.iloc[230]
# コサイン類似度で検索
cos_sim = []
for i in range(len(all_words_df)):
cos_text = all_words_df.iloc[i]
cos = np.dot(tar_text, cos_text) / np.linalg.norm(tar_text) * np.linalg.norm(cos_text)
cos_sim.append(cos)
all_words_df['cos_sim'] = cos_sim
all_words_df = all_words_df.sort_values('cos_sim', ascending=False)
all_words_df.head()
類似ツイートを表示します。
for idx in all_words_df.head(11).index[1:]:
print("**********",idx, "**********")
print(tw.iloc[idx]['ツイート本文_x'])
********** 614 ********** こちらこそありがとうございます❤ よろしくお願いします🥰 ********** 22 ********** 初回は本セット円でした 明日、で動画するので ぜひ参考にしてみてください💞🥰 ********** 588 ********** こんにちは💞 ママを敵と認識するんですね🥰 みるくもそうなのかもしれないです😌 ********** 438 ********** 毛の種類が一緒です😂 ********** 240 ********** めちゃくちゃ可愛い😍 ********** 583 ********** こちらこそありがとうございます❤ よろしくお願いします💞 これから仲良くしてくれたら嬉しい限りです🥰 ********** 239 ********** バスローブ着てみました🐶 ********** 566 ********** 夕暮れと白い犬と飼い主 公園 夕暮れ 飼い主 犬 犬好きさんと繋がりたい 犬好きな人と繋がりたい トイプードル ********** 436 ********** 🐶生まれて初めて靴下履きました😆 犬好きさんと繋がりたい ********** 311 ********** 今日も元気いっぱいみるくです🥰 ありがとうございます💞
どうもリプライのツイートだったっぽいですね。リプライはインプレッションが意外と高いのかもしれません。
WordCloud
WordCloudはよく出る単語を大きな文字で表示してくれるツールです。
以下のように、日本語フォントを指定する必要があります。
IPAゴシック等の日本語フォントを導入していない方はまず導入してください。
from wordcloud import WordCloud
fig = plt.figure(figsize=(12,10))
font_path="/Users/username/opt/anaconda3/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/ttf/ipaexg.ttf"
splitted = ' '.join([x.split('\t')[0] for x in all_words_df.columns])
wordc = WordCloud(font_path=font_path,background_color='white',width=800, height=600).generate(splitted)
plt.imshow(wordc)
plt.axis('off')
plt.show()