ProphetとTwitterで人生を振り返る


# 全Tweetで振り返る
## Tweet数で時系列分析
自分の何かを反映している気がしたので、Tweet数の変化について時系列分析してみます

- 時系列データの可視化
- (全体/年次/月次/日次)トレンドの可視化
- 変化点検知ロジックによる変化点の検出

を、Prophetに投げつけて分析します。

参考文献: [こちらのブログ](https://data.gunosy.io/entry/change-point-detection-prophet)

## データの準備 - Twitterからのデータ取得
Twitter公式からツイートを取得しようとしたら時間がかかったので、
[Twilog](https://twilog.org/settings.rb)から取得してみます。
csvで取得するとツイート内の改行の扱いが手間だったので、xmlで取得してみました。

## ライブラリの準備 - XMLの処理用
xmlの処理に必要なライブラリをインポートします。

```python
import pandas as pd
! pip install xmljson
import xmljson
from lxml import etree
```

## データのインポート
Colaboratoryで実行していますので、GoogleDriveにxmlファイルを置いて、マウントして読み込みます。

```python
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になるっぽいので、この時点で整形しておきます。

```python
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数の変化を見ます。
```python
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)
```

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>time</th>
      <th>tweet_num</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2011-11-12 21:35:08</td>
      <td>1</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2011-11-12 21:43:03</td>
      <td>2</td>
    </tr>
    <tr>
      <th>2</th>
      <td>2011-11-13 10:04:13</td>
      <td>3</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2011-11-13 11:09:01</td>
      <td>4</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2011-11-13 11:33:26</td>
      <td>5</td>
    </tr>
  </tbody>
</table>
<p>39393 rows × 2 columns</p>
</div>

できました。

## 投稿数推移の可視化
投稿数の伸びを、とりあえず可視化しておきます。

```python
import pandas as pd
import matplotlib.pyplot as plt

x = dat["time"]
y = dat["tweet_num"]

plt.plot(x,y)
```
![e8f41dff8748ca2474474af301bf8d165329b15e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5897/2889ff17-7b35-150e-5b22-0d87d0687780.png)

何回か傾きが変わっていそうです。

この辺りで人生の変化が起きていそうです。

## ライブラリの準備 - Prophetによる時系列データ分析
トレンド分析や変化点検知をするために、[Prophet](https://facebook.github.io/prophet/)を使ってみます。

```python
! 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 
$$

という形のようです。[参考](https://qiita.com/tchih11/items/42fc0d52a1486ba64b5d)

とりあえず学習させてみましょう。このmodel.fitには時間がかかります。(Colabで5分くらいかかった)
change_point_rangeは、学習に使うデータの割合(古いものからXX%を学習に使う)みたいです。
今回は予測する必要はないので、100%で学習します。n_changepointsは想定される変化点の個数？です。適当に10にしました。

```python
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)
```

## トレンドの可視化
変化の様子を可視化してみます。周期性を出してくれるみたいです。便利ですね。
```python
future = model.make_future_dataframe(periods=90)
forecast = model.predict(future)
model.plot_components(forecast)
plt.show()
```
![15eae9e7380bfcb80495c7b95ceef856b47d1309.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5897/b5819e57-af6f-9458-5225-90e48ee08dfc.png)

それぞれ眺めてみると...
- trend
    - 2013年04月くらいの鈍化は、修士の研究室配属。
    - 2015年10月くらいの鈍化は、新入社員研修後の配属かな。
    - 2018年の立ち上がりは...フラれた時期かなあ...
- weekly
    - 月曜夜に投稿数が少ないのは、多分野球の試合がないから、だと思います
- yearly
    - 3月、5月、8月に静か(休み?)
    - 11月に元気(CSと日本シリーズ?)
- daily
    - 25時くらいにピークがありそう
        - オールナイトニッポンかな
- monthly
    - 20日くらいに黙るのは...給料日？


学生時代(-2015/03)と、社会人時代(2015/04-)とは分けた方がいいかもですね。
休みの取り方が違うので。

## 変化点検知

とりあえず現状のままやってみます。

```python
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)
```
    
![33f824251284402eb3106f7d53fa5298abde99e1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5897/bc3dd7b8-f72d-fa00-5421-5b8f2e9b29e5.png)

うーん...？変化が大きな日付をピックアップして、その日に何があったかを調べてみます。
- 2012-06-17 : (増加) 院試の勉強中ですかね
- 2013-05-15 : (減少) M1のGW明けに何があったのだろう -> ぼっち飯していました 悲しい
- 2015-09-18 : (減少) 新人研修がまだ終わっていません　何の日だろう　 
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">別にぼっち飯は辛くないし <a href="https://twitter.com/hashtag/%E9%9C%87%E3%81%88%E5%A3%B0?src=hash&amp;ref_src=twsrc%5Etfw">#震え声</a></p>&mdash; はたむ (@gg_hatano) <a href="https://twitter.com/gg_hatano/status/334508246696087552?ref_src=twsrc%5Etfw">May 15, 2013</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">こくわがた 冷大HG <a href="https://twitter.com/hashtag/%E3%81%BC%E3%81%A3%E3%81%A1%E9%A3%AF?src=hash&amp;ref_src=twsrc%5Etfw">#ぼっち飯</a></p>&mdash; はたむ (@gg_hatano) <a href="https://twitter.com/gg_hatano/status/334491749726711808?ref_src=twsrc%5Etfw">May 15, 2013</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


トレンドを眺めていた時の方が、直感と合った知見が得られた気がします。

# 社会人になってからのTweetで振り返る

昔のことを覚えていないので、社会の闇に揉まれている期間のデータに絞ってみます。2015年4月からです。

```python
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()
```

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>ds</th>
      <th>y</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2015-04-01 12:14:37</td>
      <td>25999</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2015-04-01 12:17:12</td>
      <td>26000</td>
    </tr>
    <tr>
      <th>2</th>
      <td>2015-04-01 12:22:15</td>
      <td>26001</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2015-04-01 14:12:59</td>
      <td>26002</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2015-04-01 17:12:07</td>
      <td>26003</td>
    </tr>
  </tbody>
</table>
</div>

## 学習
時間かかる
```python
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)
```


はい

## トレンドの可視化
見ましょう

```python
future_work = model_work.make_future_dataframe(periods=90)
forecast_work = model_work.predict(future_work)
model_work.plot_components(forecast_work)
plt.show()
```
![fd348a06e72a3356453b03b761cf3dcb841c50e4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5897/1ba2f294-952c-5db4-8b8e-1b8a2fa6f875.png)

さっきと雰囲気が違いますね。

- trend
    - 2016年6月くらいの鈍化...あの案件ですね
    - 2017年3月くらいの鈍化...あの案件ですね
    - 2018年6月くらいの立ち上がり...はい
    - 2019年4月くらいの鈍化...あの案件
    - 2020年5月くらいの鈍化...あの案件
- weekly
    - 月曜夜に投稿が増えています。
        - 巨人をあまり見なくなって、野球の影響が減っていそうです。
- yearly
    - 5,6月に底をついて、11月がピーク
    - 毎年10月末の学会の時は、投稿伸びますね 出張中だからかな 楽しいのでしょうね
- daily
    - 25時くらいにピークがありそう オールナイトニッポンかな
- monthly
    - 20日くらいに黙るのは...給料日？なぜだろ


# まとめ
- 1人でうどんを食べた日からツイート頻度が減った
- 案件が始まるとツイート頻度が減る
- 給料日付近で黙る
- 明示的なライフイベントを変化点として入力する案もある