全Tweetで振り返る
Tweet数で時系列分析
自分の何かを反映している気がしたので、Tweet数の変化について時系列分析してみます
- 時系列データの可視化
- (全体/年次/月次/日次)トレンドの可視化
- 変化点検知ロジックによる変化点の検出
を、Prophetに投げつけて分析します。
参考文献: こちらのブログ
データの準備 - Twitterからのデータ取得
Twitter公式からツイートを取得しようとしたら時間がかかったので、
Twilogから取得してみます。
csvで取得するとツイート内の改行の扱いが手間だったので、xmlで取得してみました。
ライブラリの準備 - XMLの処理用
xmlの処理に必要なライブラリをインポートします。
import pandas as pd
! pip install xmljson
import xmljson
from lxml import etree
データのインポート
Colaboratoryで実行していますので、GoogleDriveにxmlファイルを置いて、マウントして読み込みます。
xml_tree = etree.parse('/content/drive/MyDrive/works/gg_hatano220206.xml')
# すべてのタグの取得
xml_root = xml_tree.getroot()
# xmlデータをdict型に変換
xml_dict = xmljson.yahoo.data(xml_root)
データの整形
xml > tweets > tweet > time に投稿時刻が入っていたので、取得します。
先頭に"20"をつけるとYYYYmmddになるっぽいので、この時点で整形しておきます。
from datetime import datetime as dt
tweet_time_list = []
for d in xml_dict['tweets']['tweet']:
tmp = '20' + d['time']
tmp = dt.strptime(tmp, '%Y%m%d %H%M%S')
tweet_time_list.append(tmp)
時間順に並べて番号を付与することで、Tweet数の変化を見ます。
dat = pd.DataFrame({'time' : tweet_time_list})
dat = dat.sort_values('time', ascending=True).reset_index(drop = True)
dat = dat.assign(tweet_num=range(len(dat)))
dat['tweet_num'] = dat['tweet_num'] + 1
dat.head(5)
time | tweet_num | |
---|---|---|
0 | 2011-11-12 21:35:08 | 1 |
1 | 2011-11-12 21:43:03 | 2 |
2 | 2011-11-13 10:04:13 | 3 |
3 | 2011-11-13 11:09:01 | 4 |
4 | 2011-11-13 11:33:26 | 5 |
39393 rows × 2 columns
できました。
投稿数推移の可視化
投稿数の伸びを、とりあえず可視化しておきます。
import pandas as pd
import matplotlib.pyplot as plt
x = dat["time"]
y = dat["tweet_num"]
plt.plot(x,y)
何回か傾きが変わっていそうです。
この辺りで人生の変化が起きていそうです。
ライブラリの準備 - Prophetによる時系列データ分析
トレンド分析や変化点検知をするために、Prophetを使ってみます。
! pip install fbprophet
from fbprophet import Prophet
学習
Prophetのモデル式は、トレンド$g_t$、季節$s_t$、イベント$h_t$、誤差$\epsilon_t $として、
$$
y_t =g_t + s_t + h_t + \epsilon_t
$$
という形のようです。参考
とりあえず学習させてみましょう。このmodel.fitには時間がかかります。(Colabで5分くらいかかった)
change_point_rangeは、学習に使うデータの割合(古いものからXX%を学習に使う)みたいです。
今回は予測する必要はないので、100%で学習します。n_changepointsは想定される変化点の個数?です。適当に10にしました。
dat = dat.rename(columns={'time':'ds', 'tweet_num':'y'})
model = Prophet(n_changepoints = 10, changepoint_range=1.0, growth='linear')
model.add_seasonality(name='monthly', period=30.5, fourier_order=5)
model.fit(dat)
トレンドの可視化
変化の様子を可視化してみます。周期性を出してくれるみたいです。便利ですね。
future = model.make_future_dataframe(periods=90)
forecast = model.predict(future)
model.plot_components(forecast)
plt.show()
それぞれ眺めてみると...
- trend
- 2013年04月くらいの鈍化は、修士の研究室配属。
- 2015年10月くらいの鈍化は、新入社員研修後の配属かな。
- 2018年の立ち上がりは...フラれた時期かなあ...
- weekly
- 月曜夜に投稿数が少ないのは、多分野球の試合がないから、だと思います
- yearly
- 3月、5月、8月に静か(休み?)
- 11月に元気(CSと日本シリーズ?)
- daily
- 25時くらいにピークがありそう
- オールナイトニッポンかな
- 25時くらいにピークがありそう
- monthly
- 20日くらいに黙るのは...給料日?
学生時代(-2015/03)と、社会人時代(2015/04-)とは分けた方がいいかもですね。
休みの取り方が違うので。
変化点検知
とりあえず現状のままやってみます。
import seaborn as sns
df_input = dat
# add change rates to changepoints
df_changepoints = df_input.loc[model.changepoints.index]
df_changepoints['delta'] = model.params['delta'].ravel()
# get changepoints
df_changepoints['ds'] = df_changepoints['ds'].astype(str)
df_changepoints['delta'] = df_changepoints['delta'].round(2)
df_selection = df_changepoints[df_changepoints['delta'] != 0]
date_changepoints = df_selection['ds'].astype('datetime64[ns]').reset_index(drop=True)
# plot
sns.set(style='whitegrid')
ax = sns.factorplot(x='ds', y='delta', data=df_changepoints, kind='bar', color='royalblue', size=4, aspect=2)
ax.set_xticklabels(rotation=90)
うーん...?変化が大きな日付をピックアップして、その日に何があったかを調べてみます。
- 2012-06-17 : (増加) 院試の勉強中ですかね
- 2013-05-15 : (減少) M1のGW明けに何があったのだろう -> ぼっち飯していました 悲しい
- 2015-09-18 : (減少) 新人研修がまだ終わっていません 何の日だろう
別にぼっち飯は辛くないし #震え声
— はたむ (@gg_hatano) May 15, 2013
こくわがた 冷大HG #ぼっち飯
— はたむ (@gg_hatano) May 15, 2013
トレンドを眺めていた時の方が、直感と合った知見が得られた気がします。
社会人になってからのTweetで振り返る
昔のことを覚えていないので、社会の闇に揉まれている期間のデータに絞ってみます。2015年4月からです。
dat_work = dat[dat['ds'] > dt.strptime("20150401 000000", '%Y%m%d %H%M%S')]
dat_work = dat_work.reset_index(drop=True)
dat_work.head()
ds | y | |
---|---|---|
0 | 2015-04-01 12:14:37 | 25999 |
1 | 2015-04-01 12:17:12 | 26000 |
2 | 2015-04-01 12:22:15 | 26001 |
3 | 2015-04-01 14:12:59 | 26002 |
4 | 2015-04-01 17:12:07 | 26003 |
学習
時間かかる
model_work = Prophet(changepoint_range=1.0, growth='linear')
model_work.add_seasonality(name='monthly', period=30.5, fourier_order=5)
model_work.fit(dat_work)
はい
トレンドの可視化
見ましょう
future_work = model_work.make_future_dataframe(periods=90)
forecast_work = model_work.predict(future_work)
model_work.plot_components(forecast_work)
plt.show()
さっきと雰囲気が違いますね。
- trend
- 2016年6月くらいの鈍化...あの案件ですね
- 2017年3月くらいの鈍化...あの案件ですね
- 2018年6月くらいの立ち上がり...はい
- 2019年4月くらいの鈍化...あの案件
- 2020年5月くらいの鈍化...あの案件
- weekly
- 月曜夜に投稿が増えています。
- 巨人をあまり見なくなって、野球の影響が減っていそうです。
- 月曜夜に投稿が増えています。
- yearly
- 5,6月に底をついて、11月がピーク
- 毎年10月末の学会の時は、投稿伸びますね 出張中だからかな 楽しいのでしょうね
- daily
- 25時くらいにピークがありそう オールナイトニッポンかな
- monthly
- 20日くらいに黙るのは...給料日?なぜだろ
まとめ
- 1人でうどんを食べた日からツイート頻度が減った
- 案件が始まるとツイート頻度が減る
- 給料日付近で黙る
- 明示的なライフイベントを変化点として入力する案もある