【要素分解入門】時系列解析の手法をRとpythonで並べてみる♬

今回は、前回の時系列分解をさらに追及してみた。
すなわち、モデルは以下のとおり
$Y_t=T_t + S_t + e_t$
または、
$Y_t=T_tS_te_t$
として、時系列データを分解するdecompose()手法である。
前回は、それぞれのデータを同時描画していたが、今回は例えばノイズ除去した純粋な音声や振動などの再生や利用を目的に、それぞれ保存したり再生したいという動機から実施してみた。
データは参考③のサイトの死亡数の速報値と参考④の2018年までの過去データである。
今回の参考は以下のとおりである。
参考①はR言語のものであり、参考②はpythonのものである。
ここでは独立に数値化してcsvファイルに保存するまでをまとめる。
【参考】
①[Extracting Seasonality and Trend from Data: Decomposition Using R](https://anomaly.io/seasonal-trend-decomposition-in-r/index.html)
②[TIME SERIES DECOMPOSITION & PREDICTION IN PYTHON](https://www.pythonforfinance.net/2019/07/22/time-series-decomposition-prediction-in-python/)
③[統計で見る日本 e-Statは、日本の統計が閲覧できる政府統計ポータルサイトです](https://www.e-stat.go.jp/stat-search/files?page=1&layout=datalist&toukei=00450011&tstat=000001028897&cycle=1&tclass1=000001053058&tclass2=000001053059&result_back=1)
④[データセット一覧(5-4死亡月別にみた年次別死亡数及び死亡率（人口千対）)@統計で見る日本](https://www.e-stat.go.jp/stat-search/files?page=1&layout=datalist&toukei=00450011&tstat=000001028897&cycle=7&year=20180&month=0&tclass1=000001053058&tclass2=000001053061&tclass3=000001053065&result_back=1)
なお、今回のコードとデータは以下に置きました。
・[MuAuan/R_Audio](https://github.com/MuAuan/R_Audio)
###やったこと
・Rでのdecompose
・pythonでのdecompose
###・Rでのdecompose
前回のほぼまんまですが、decompose_IIP$seasonalという表記でseasonalのデータを指定できます。

```R
data <- read.csv("./industrial/sibou_p.csv",encoding = "utf-8")
IIP <- ts(data,start=c(2008),frequency=12)
w_list <- c("seasonal","random","trend","observed")
decompose_IIP <- decompose(IIP)
for (i in w_list){
  if (i=="seasonal"){
    plot(decompose_IIP$seasonal)
  }else if(i=="random"){
    plot(decompose_IIP$random)
  }else if(i=="trend"){
    plot(decompose_IIP$trend)
  }else if(i=="observed"){
    plot(IIP)
  }
}
```

|seasonal|random|
|:-:|:-:|
|![IIP_compose_sibou_seasonal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/e070d7cf-d7e5-87be-fdc2-e67e65ca36fb.png)|![IIP_compose_sibou_random.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/dc1c04d5-b0dd-57a8-1761-723120348f1e.png)|
|trend|observed|
|![IIP_compose_sibou_trend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/099b9f01-3475-5b51-2680-20340c1efe67.png)|![IIP_compose_sibou_observed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/e26c1d53-eb9b-aa76-7da0-191feef77a33.png)|
もう一つのコード
まず、オリジナルデータを描画します。
次に、今回のデータは12か月周期なのでトレンド曲線として12移動平均を取ります。
そして、上記のオリジナルデータに重畳してlinesを引きます。

```R
#install.packages("forecast")
library(forecast)
plot(as.ts(IIP))

trend_beer = ma(IIP, order = 12, centre = T) #4
lines(trend_beer)
```
![plot_line_original.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/7f548394-7901-572d-6e1c-09bb1c04f130.png)


plotはそのトレンド曲線だけを描画しています。

```R
plot(as.ts(trend_beer))
```
![trend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/954b3194-ac20-7178-4266-55b8cc2e4b88.png)
オリジナルデータからトレンドデータを引き算してプロットします。

```R
detrend_beer = IIP - trend_beer
plot(as.ts(detrend_beer))
```
![detrend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/ec193f6e-c1ad-c346-34b0-f74af3d1a9e7.png)
そして、トレンドを差し引いたノイズが乗っているデータを12個ずつのデータに区分して、それらを平均したものを季節変動として計算します。
それを13個連ねて描画しています。
【参考】
・[Row and Column Summaries](https://docs.tibco.com/pub/enterprise-runtime-for-R/5.0.0/doc/html/Language_Reference/base/colMeans.html)
・[matrix](https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/matrix)

```R
m_beer = t(matrix(data = detrend_beer, nrow = 12))
seasonal_beer = colMeans(m_beer, na.rm = T)
plot(as.ts(rep(seasonal_beer,13)))
```
![seasonal_bear13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/595a1414-5c86-bfe8-39f8-4a314e80febb.png)
最後に、ランダムな変動分を求めます。
結果はdecompose()関数で求めたものと一致しました。
**こんな計算で求めている**んですね。

```R
random_beer = IIP - trend_beer - seasonal_beer
plot(as.ts(random_beer))
```
![calc_random.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/d411b2bd-fe56-239c-87cf-f0e119c660ed.png)
最後にいろいろ利用するために、以下のようにcsvファイルに出力します。

```R
write.csv(as.ts(rep(seasonal_beer,1300)), file = 'decompose_seasonal1300.csv')
```
###・pythonでのdecompose
pythonでも同じような関数が用意されています。
コードは以下のとおりです。

```py
import pandas as pd
import statsmodels.api as sm
from statsmodels.tsa.seasonal import STL
import matplotlib.pyplot as plt

# Set figure width to 12 and height to 9
plt.rcParams['figure.figsize'] = [12, 9]
df = pd.read_csv('sibou_.csv')
series = df['Price']
print(series)

cycle, trend = sm.tsa.filters.hpfilter(series, 144)
fig, ax = plt.subplots(3,1)
ax[0].plot(series)
ax[0].set_title('Price')
ax[1].plot(trend)
ax[1].set_title('Trend')
ax[2].plot(cycle)
ax[2].set_title('Cycle')
plt.show()
```
![Figure_1_py.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/ae595412-cfc1-989d-387c-8b8e1bf9cab2.png)

pythonでももう一つの方法
個人的には、分解の結果は上の方法だとcycleに対してもう一段の処理が必要で、こちらはランダム分の処理までできているのでこちらの方がリーズナブルに見えます。
また、Rと比較しても、少し異なりseasonalは年々振動幅が大きくなっている様子を見せています（Rはこの変動を平均化処理したので消えているのは当たり前ですが。。。）。場合により、この手法が一番汎用性が高いと言えると思います。
【参考】
・[Seasonal-Trend decomposition using LOESS (STL)](https://www.statsmodels.org/dev/examples/notebooks/generated/stl_decomposition.html)

```py
stl=STL(series, period=12, robust=True)
result = stl.fit()
chart = result.plot()
chart1= result.plot(observed=False, resid=False)
chart2= result.plot(trend=False, resid=False)
plt.show()
```
Observed & Trend & Seasonal & Residual
![Figure_2_py.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/c60aa7b5-ba99-93f0-baef-b62151e4e308.png)
Trend & Seasonal
![Figure_3_py.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/372b7f0c-0870-5a79-d2bf-c4026f6166b1.png)
Obeserved & Seasonal
![Figure_4_py.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/8388cff6-e8a5-aa6c-91b4-b924fa55a53f.png)
また、以下のようにすると、ばらばらのグラフとして一つのグラフに出力できます。
※pl1...などの数値データを利用するため変数に代入しています

```py
pl1 = result.observed
pl2 = result.trend
pl3 = result.seasonal
pl4 = result.resid

plt.plot(pl1)
plt.plot(pl2)
plt.plot(pl3)
plt.plot(pl4)
plt.show()
plt.close()
```
![pl1-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/bfc1cfb6-f632-aa08-bae4-4cc6c9eb86a1.png)

また、以下のようにすると簡単に出力できます。

```py
pl5 = result.seasonal.plot()
plt.show()
plt.close()
...
```
| seasonal |resid|
|:-:|:-:|
|![seasonal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/596ee5d7-7175-acd9-e470-5e9599c6c211.png)|![resid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/e3d28014-5411-fd6f-7419-2231837fca18.png)|
|trend|observed|
|![trend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/3d74c30a-e8e0-0d85-f294-3241b3fd7c29.png)|![observed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/d52232bd-ff65-88bb-8671-81685b526348.png)|


そして、csvファイル出力は以下のようにやります。

```py
import csv
with open('sample_pl2.csv', 'w') as f:
    writer = csv.writer(f, delimiter='\n')
    writer.writerow(pl2)
```
###まとめ
・Rとpythonでdecompose()のやり方を並べてみた
・それぞれのコードは結果は少し異なるがほぼ同等のことが出来て、ノイズやトレンドが乗った周期的な変動を純粋な周期的な変動として取り出すことが出来た
・各要素分解された成分をcsvデータに書き出すことが出来、二次利用出来る
・実は今回は死亡数の経年変化を例として[3月までの速報値](https://www.e-stat.go.jp/stat-search/files?page=1&layout=datalist&toukei=00450011&tstat=000001028897&cycle=1&tclass1=000001053058&tclass2=000001053059&result_back=1)を使ったが、その範囲では2011年のような異常値は見えていない（むしろ減少傾向）

・次に、周期的な変動要素を音に変換し聞いてみることとする
・非周期な変動要素についても（検定はともかく）やってみる