美容師歴13年の36歳が人生初めて株価分析やってみた結果

:::note info
このブログはAidemy Premiumのカリキュラムの一環で、受講修了条件を満たすために公開しています
:::
◇自己紹介

元々分析の仕事に興味があり、昨今より重要度を増しているデータ分析・機械学習およびPythonについて理解を深め、また今後データ分析の分野に転職したいと思い、Aidemy Premium「データ分析コース」を6ヶ月受講いたしました。

◇本記事の概要

どんな人に読んで欲しいか
　Pythonの概要は何となく知っているが、コードを書いたり実行してみたことはない方。
この記事に書くこと、わかること
　『回帰分析』(すごく簡単な部分だけ)を実際に自分の手で動かす過程を知ることができます。
この記事で扱わないこと、注意点
　株価の予測を扱いますが、数字上だけのものです。
　株価予測には本来様々な要素を考慮して予測を立てることが必要ですが、その内容を盛り込んではおりません。

 ■ 概要

分析内容：楽天の株価データを用いて、ある日の終値を予想する分析を行なう

簡単に取得できるような株価データを用いて、回帰分析を行っていきます。
最終的にその精度も出せればと思っています。

 ■実行環境
Google colaboratory

■ 目的

本当は自社のデータをSQLで抜いてきて時系列分析や需要予測分析なんて壮大なことをしてみたいですが、
まず第一歩として、 簡単でも何かを完成させるのが大事かなとも思うので、
まずは完成させてみよう！ を目的にしています。


■ 早速、やっていきます。

...の前に、分析の流れを確認します。

(1)データの準備
⇒今回は楽天の株価データを使わせていただきます。
何とかして、データをcsvで持ってきます。
(2)データの加工
⇒持ってきたデータを、使える形に加工したり、不要部分を削除したりします。
(3)データの分析
⇒データを訓練用とテスト用に分割し、いくつかの機械学習モデルを適用して予測。線形回帰、Lasso回帰、Ridge回帰、ElasticNet回帰など、複数のモデルを試してみます。また、LightGBMも使用します。
(4)精度を確認する
⇒各モデルの予測精度を比較し、最も適したモデルを特定
このような流れで対応したいと思います。

## (1)データの準備


株価取得コード

・pandas_datareader (経済データや株価等を取得できるライブラリ)
・yfinance (Yahoo!ファイナンスから情報を取得するライブラリ)

を用いて、以下のコードで直接取得することが可能です。
```python
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()

#変数dataに楽天('4755')の株価データを代入。期間はstart,endで指定
data = pdr.get_data_yahoo('4755.T', start='2023-05-01', end='2024-05-01')
data.head() #データ内容の確認
```
まだまだ慣れないのでdata.head()でデータの内容をこまめに確認していきます。
その内容は以下になります。

![スクリーンショット 2024-05-12 16.40.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785901/a0592351-2b75-2604-bf4e-e3264190eac1.png)


終値には「Close」と「Adj Close」がありますが、
後者が"調整後の終値"になりますので、こちらを目的変数として使いたいと思います。

## (2)データの加工

持ってきたデータを、使える形に加工したり、不要部分を削除したりします。

一般的に、Volume(出来高)が関係してくるといわれていますので、出来高を使いたいと思います。
例えば、「出来高を週で合計すると翌日の予測ができたりしないかな」と考えてみた際のコードが以下になります。
```python
#--元データの基本準備
# カラムから不要項目を削除。'始値', '高値', '安値','終値(Closeのほう)'
data_chart = data.drop(['Open', 'High', 'Low','Close'], axis=1)
# 日付順に昇順に並べておく。
data_chart = data_chart.sort_values(['Date'])

#--説明変数列＆目的変数(ラベル列)追加
#'Volume_WeekSum'列を追加し、当日までの7日間(約1週間)の出来高を記載
data_chart['Volume_WeekSum'] = data_chart['Volume'].rolling(window=7).sum()
#'label'列を追加し、その日の"翌日"(1日後)の'調整後終値'を記載
data_chart['label'] = data_chart['Adj Close'].shift(-1)

#通常head()の5行だけだと7日分合計されているか見えないため、10行表示
data_chart.head()
```



![スクリーンショット 2024-05-10 22.04.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785901/4978d8a6-590e-4905-1fa5-a119a9d3a064.png)


あとは先頭の6行と、最終行1行に欠損値が出るので、削除しておきます。
```python
data_chart=data_chart.dropna()
data_chart
```

![スクリーンショット 2024-05-12 16.46.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785901/8e0fa02e-b2ad-91cb-2f88-25e442b62842.png)

## 3)データの分析

訓練データ、テストデータを作成していきます。
```python
from sklearn.model_selection import train_test_split
# xに説明変数(元データから目的変数列を抜いたdataset)を、yに目的変数(調整後終値)を格納
x = data_chart.drop(['label'],axis=1)
y = data_chart.values[:, 3]
#train_test_splitを使って、「訓練データ」と「検証データ」を8:2に分けます。(test_size=20%)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42, shuffle=False)
```

それでは5つの回帰モデルを使って分析していきます。
```python
#各種必要モジュールをインポート(追加分のみ)
from sklearn.linear_model import Lasso
from sklearn.linear_model import Ridge
from sklearn.linear_model import ElasticNet
from sklearn.linear_model import LinearRegression
import lightgbm as lgb
from sklearn.metrics import mean_squared_error

#結果表示用に、「もっともよいRMSEのモデルの値」「その名前」を入れる変数
 #（それぞれUseModelRMSE,UseModelName）の初期化
UseModelRMSE = 0
UseModelName  = ""

# 線形回帰---------
model_Linear = LinearRegression()
model_Linear.fit(x_train,y_train)
y_pred_Linear = model_Linear.predict(x_test)

# RMSEを出力
print("LinearRegression:{}".format(mean_squared_error(y_test,y_pred_Linear ,squared=False)))

# 先頭なのでそのまま値を入れる
UseModelRMSE = mean_squared_error(y_test, y_pred_Linear,squared=False)
UseModelName  = "LinearRegression"

# ラッソ回帰---------
model_Lasso = Lasso()
model_Lasso.fit(x_train,y_train)
y_pred_Lasso = model_Lasso.predict(x_test)
rmse_Lasso = mean_squared_error(y_test,y_pred_Lasso ,squared=False)
# RMSEを出力
print("Lasso:{}".format(rmse_Lasso))

# UseModelRMSEと比較し、スコアが高ければ上書き
if UseModelRMSE > rmse_Lasso:
    UseModelRMSE = rmse_Lasso
    UseModelName  = "Lasso"


# リッジ回帰---------
model_Ridge = Ridge()
model_Ridge.fit(x_train,y_train)
y_pred_Ridge = model_Ridge.predict(x_test)
rmse_Ridge = mean_squared_error(y_test,y_pred_Ridge ,squared=False)
# RMSEを出力
print("Ridge:{}".format(rmse_Ridge))

#　UseModelRMSEと比較し、スコアが高ければ上書き
if UseModelRMSE > rmse_Ridge:
    UseModelRMSE =rmse_Ridge
    UseModelName  = "Ridge"

# ElasticNet回帰---------
model_ElasticNet = ElasticNet()
model_ElasticNet.fit(x_train,y_train)
y_pred_ElasticNet = model_ElasticNet.predict(x_test)
rmse_ElasticNet= mean_squared_error(y_test,y_pred_ElasticNet,squared=False)
# RMSEを出力
print("ElasticNet:{}".format(rmse_ElasticNet))

# UseModelRMSEと比較し、スコアが高ければ上書き
if UseModelRMSE > rmse_ElasticNet:
    UseModelRMSE = rmse_ElasticNet
    UseModelName  = "ElasticNet"




print("最もRMSEが低いモデルは {0} で、その値は {1} でした".format(UseModelName,UseModelRMSE))

```
![スクリーンショット 2024-05-12 20.35.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785901/10ac9c1b-c670-1276-5388-86eae46fbeae.png)

2  Light GBMで実装
重回帰モデルよりもより複雑な予測ができるLight GBMを試してみます。
LightGBM は、2016年に米マイクロソフト社が公開した勾配ブースティングに基づく機械学習手法です。
より気になる方は調べて見て下さい。
実装の流れとして大まかには、Datasetオブジェクトの作成 -> パラメータ辞書の作成 -> train()で学習といった流れになります。

```python
# Datasetオブジェクトの作成 (異なるポイント①)
#XGBoostで学習するためのデータ形式に変換
d_train = lgb.Dataset(data=x_train,label=y_train)
d_test = lgb.Dataset(data=x_test,label=y_test)

# パラメータ設定
params = {
    'objective':'regression', # 二値分類タスク(今回は回帰なのでregression)
    'metric':'rmse', # objectiveを定義しているので自動入力されるが、明示しておく
    'n_estimators':10000, # early_stopping使用時は大きな値
    'verbosity': -1, # コマンドライン出力しない設定
    'random_state':42
}

# モデルの構築・学習 (異なるポイント②)
gbm = lgb.train(params, d_train, valid_sets=[d_test], # early_stoppingの評価用データ
                callbacks=[lgb.early_stopping(stopping_rounds=10, verbose=True)] # early_stoppingは数値が変わらないなら10回で止まるという意味
               )

# テストデータで予測
y_pred = gbm.predict(x_test)


rmse_lgb=mean_squared_error(y_test, y_pred,squared=False) #squaredは二乗という意味　Falseは二乗しない
print("LightGBM:{}".format(rmse_lgb))

if UseModelRMSE > rmse_lgb:
    UseModelRMSE = rmse_lgb
    UseModelName  = "LightGBM"




```
![スクリーンショット 2024-05-16 18.41.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785901/96247d69-115c-41df-0c43-8dfb0ed3ff44.png)

最終的に一番良かったモデルを表示します。

```python
print("最もRMSEが低いモデルは {0} で、その値は {1} でした".format(UseModelName,UseModelRMSE))
```
![スクリーンショット 2024-05-16 18.27.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785901/b1e67cf9-91ca-a93b-fe70-996bd349b4f9.png)


## 結論
線形回帰、Lasso回帰、Ridge回帰、ElasticNet回帰、およびLightGBMという5つのモデルを比較してみた結果、線形回帰が最も低いRMSE値を達成しました。
一般的にはLightBGMのモデルが一番高くなる可能性がありますが、今回データ量が少ないことによる過学習に陥ってしまい精度が低くなってしまった可能性があります。
より精度を高めるためには、データ量を増やすことだったり、株価以外のデータも加えてあげることによりさらに精度が高くなる可能性があります。
また、より時系列に特化したモデル（LSTM)を使用することで精度が上がる可能性があります。

しかしながら、先に述べたように株価の分析それ自体には数字のみならず社会情勢や経済の状況を含め多面的に検討する必要があり、これが実際に使える株価予測とはとても言えません。

## 今後の活用
今回はまず"データ分析の第一歩"というところで簡単な処理をやってみました。


Pythonや機械学習についてプログラミング初心者の僕でも挫折せず体系立てて学べ、実践向きな勉強の機会を与えていただいて世界が一気に広がりました。
今後はより専門的な時系列解析や感情分析なども理解を深めてもっと本格的な分析ができたら嬉しいと思いました。