３４歳未経験で超めんどくさがりな人間が株価分析に挑戦してみた結果

# はじめに

⚫︎自己紹介

 はじめまして。マオと申します。34歳です。この歳になって、データ分析を学びたいと思うようになりました。そのきっかけは、二人の兄弟の存在が大きいです。
弟は大学卒業後、IT業界で数年間働いており、兄は僕自身より少し先にエンジニアスクールにて、データ分析コースを受講しています。
二人がITの分野で活躍している姿を見て、僕も徐々にITに興味を持つようになりました。
昔から兄弟三人で起業するという夢があり、このタイミングしかないと思い立ち、僕もITの勉強を始めることにしました。
ITにはさまざまな分野がありますが、僕は色々な人とコミュニケーションを取りながら進められるデータ分析に惹かれ、「データ分析コース」をエンジニアスクールにて6ヶ月間受講しました。
学んだことを記録し、可視化することで、学びを深めたいと思い、このブログを始めました。


⚫︎本記事の概要

このブログ記事では、株価データを用いて簡単な「回帰分析」を実施する方法を解説します。楽天の株価データを使用して、特定の日の終値を予測するための分析を行います。手に入れやすい株価データを用いて、「回帰分析」の基本的な手法を学び、実際にモデルを作成してその精度を評価することを目指します。
数カ月前まで僕自身が完全な初心者だった経験を踏まえ、同じような初心者の方々に参考になれば幸いです。


⚫︎目的

最終的な目標としては、自社のデータを用いて高度な分析を行いたいと考えています。しかし、その第一歩として、まずは簡単な株価予測モデルを作成し、何かを「完成させる」経験を重視したいと考えています。初心者が学ぶ上で、小さな成功を積み重ねることが重要だと考えていますので、まずは基本を学びながら進めていきたいと考えてます。


⚫︎読んでほしい方

Pythonの基本的な知識はあるが、実際にコードを書いて実行したことがない方。
データ分析や株価予測に興味があるが、どこから始めればよいかわからない方。


⚫︎この記事で学べること

株価データを使った「回帰分析」の基礎的な手法を、自分の手で実際に動かすプロセスを学べます。


⚫︎注意点

この記事で取り扱う株価予測は、あくまでデータ上のものであり、実際の投資判断には使用できません。
株価の予測には通常、多くの要素を考慮する必要がありますが、本記事ではそのような複雑な要素は取り扱いません。


⚫︎実行環境

Google Colaboratory
Google Colabは、ブラウザ上でPythonコードを実行できる無料の環境です。Pythonのインストールが不要で、すぐにデータ分析を始められるため、初心者にとっても最適なツールとなっているためこれを利用します。


## データ分析の流れ

それでは、具体的な手順を紹介します。

① データの準備

楽天の株価データを収集し、CSV形式で取得します。

② データの加工

取得したデータを分析に適した形に整え、不要な部分を削除します。

③ データの分析

データを訓練用とテスト用に分割し、複数の機械学習モデルを適用して予測を行います。具体的には、線形回帰、Lasso回帰、Ridge回帰、ElasticNet回帰を行い、さらにはLightGBMにも挑戦してみたいと思ってます。

④ 精度の評価

各モデルの予測精度を比較し、最も性能の良いモデルを特定します。

# 実装コードの全体



```python
import yfinance as yf

# 楽天の株価データを取得するためにTickerオブジェクトを作成
ticker = yf.Ticker("4755.T")

# 指定した期間の株価データを取得
data = ticker.history(start="2023-08-30", end="2024-08-30")

# データの最初の5行を表示
print(data.head())

# 不要なカラムを削除
data_chart = data.drop(['Open', 'High', 'Low', 'Dividends', 'Stock Splits'], axis=1)

# 日付順に並べ替え
data_chart = data_chart.sort_index()

# 7日間の出来高の合計を新しい列 'Volume_WeekSum' に追加
data_chart['Volume_WeekSum'] = data_chart['Volume'].rolling(window=7).sum()

# 翌日の終値を 'label' 列に追加（予測のための目的変数）
data_chart['label'] = data_chart['Close'].shift(-1)

# データの確認と通常のheadだと５日間しか表示されない為１０日間表示する
print(data_chart.head(10))

# 欠損値を削除
data_chart = data_chart.dropna()

print(data_chart.head(10))

from sklearn.model_selection import train_test_split

# 特徴量（説明変数）として 'Volume_WeekSum' を使用し、目的変数（ラベル）として 'label' を設定
X = data_chart[['Volume_WeekSum']]  # 説明変数
y = data_chart['label']             # 目的変数

# 訓練データとテストデータに分割（80%を訓練データ、20%をテストデータ）
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 訓練データとテストデータのサイズを確認
print("訓練データのサイズ (X_train, y_train):", X_train.shape, y_train.shape)
print("テストデータのサイズ (X_test, y_test):", X_test.shape, y_test.shape)

# 訓練データの最初の5行を表示
print("\n訓練データ (X_train) の最初の5行:")
print(X_train.head())

print("\n訓練データ (y_train) の最初の5行:")
print(y_train.head())

# テストデータの最初の5行を表示
print("\nテストデータ (X_test) の最初の5行:")
print(X_test.head())

print("\nテストデータ (y_test) の最初の5行:")
print(y_test.head())

#各種必要モジュールをインポート(追加分のみ)
from sklearn.linear_model import Lasso
from sklearn.linear_model import Ridge
from sklearn.linear_model import ElasticNet
from sklearn.linear_model import LinearRegression
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))

import lightgbm as lgb

# 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"

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

```
次からは各セクションごとに説明していきたいと思います。


# データの準備

yfinance　の Ticker オブジェクトを使用し、特定の株式や証券の情報を取得します。Ticker オブジェクトを使うことで、企業の株価データ、財務情報、配当情報など、さまざまなデータを簡単に取得することができます。
```python
import yfinance as yf

# 楽天の株価データを取得するためにTickerオブジェクトを作成
ticker = yf.Ticker("4755.T")

# 指定した期間の株価データを取得
data = ticker.history(start="2023-08-30", end="2024-08-30")

# データの最初の5行を表示
print(data.head())

# データの最初の5行を表示
print(data.head())
```
初心者なので、data.head()でデータの内容を確認します。
その内容は以下になります。

![スクリーンショット 2024-09-09 17.52.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3873694/236e1cd2-223d-dc59-d697-3287939a3cdb.png)




持ってきたデータを、使える形に加工したり、不要部分を削除したりします。
そして、出来高の7日間の合計を計算し、翌日の終値を予測するためのラベルを作成したいと思います。

```python
# 不要なカラムを削除
data_chart = data.drop(['Open', 'High', 'Low', 'Dividends', 'Stock Splits'], axis=1)

# 日付順に並べ替え
data_chart = data_chart.sort_index()

# 7日間の出来高の合計を新しい列 'Volume_WeekSum' に追加
data_chart['Volume_WeekSum'] = data_chart['Volume'].rolling(window=7).sum()

# 翌日の終値を 'label' 列に追加（予測のための目的変数）
data_chart['label'] = data_chart['Close'].shift(-1)

# データの確認と通常のheadだと５日間しか表示されない為１０日間表示する
print(data_chart.head(10))
```
![スクリーンショット 2024-09-09 17.57.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3873694/78553098-b634-5a63-09f1-0dbf16f33255.png)



欠損値が発生するので削除します。

```python
data_chart = data_chart.dropna()
print(data_chart.head(10))
```
![スクリーンショット 2024-09-09 17.58.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3873694/7287d4e6-8050-b8fc-8383-dd07f64ac887.png)


## データの分析

上記のデータを利用してデータ分析を行います。
まず、訓練データと　テストデータを作成します。
適切なデーター量を確保するために、８０%を訓練用、２０%をテスト用と分割します。
一般に80:20の分割はバランスが良いとされ、訓練データが十分に多いとモデルがしっかり学習できます。一方、テストデータが少なすぎるとモデルの評価が不安定になる可能性があるため、20%をテスト用に確保します。
```python
from sklearn.model_selection import train_test_split

# 特徴量（説明変数）として 'Volume_WeekSum' を使用し、目的変数（ラベル）として 'label' を設定
X = data_chart[['Volume_WeekSum']]  # 説明変数
y = data_chart['label']             # 目的変数

# 訓練データとテストデータに分割（80%を訓練データ、20%をテストデータ）
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
```
ここでも初心者のためデータの確認をします。
訓練データをテストデータ、そしてデータのサイズを確認します。
```python
# 訓練データとテストデータのサイズを確認
print("訓練データのサイズ (X_train, y_train):", X_train.shape, y_train.shape)
print("テストデータのサイズ (X_test, y_test):", X_test.shape, y_test.shape)

# 訓練データの最初の5行を表示
print("\n訓練データ (X_train) の最初の5行:")
print(X_train.head())

print("\n訓練データ (y_train) の最初の5行:")
print(y_train.head())

# テストデータの最初の5行を表示
print("\nテストデータ (X_test) の最初の5行:")
print(X_test.head())

print("\nテストデータ (y_test) の最初の5行:")
print(y_test.head())
```
![スクリーンショット 2024-09-09 18.03.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3873694/954487fa-34f9-27e6-6032-be28da0ce3d0.png)



それではまず、線形回帰、Lasso回帰、Ridge回帰、ElasticNet回帰の４つのモデルで分析していきたいと思います。
```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
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-09-09 18.10.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3873694/a2acf06b-d233-4068-6b0c-7126b651ba93.png)


そして、重回帰モデルよりもより複雑な予測ができるLight GBMも試してみたいと思います。
LightGBM は非常に高速で、特に大規模データセットに対して優れたパフォーマンスを発揮し、学習時間を大幅に短縮できます。
実装の流れとして大まかには、Datasetオブジェクトの作成 -> パラメータ辞書の作成 -> train()で学習といった流れになります。
```python
import lightgbm as lgb

# 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"
```
# 精度の評価


最後に一番良かったモデルを表示します。
```python
print("最もRMSEが低いモデルは {0} で、その値は {1} でした".format(UseModelName,UseModelRMSE))
```
![スクリーンショット 2024-09-09 18.18.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3873694/a57a856c-aa09-363d-87af-8f463d3be4c0.png)

線形回帰、Lasso回帰、Ridge回帰、ElasticNet回帰、およびLightGBMという5つのモデルを比較してみた結果、LightGBMが最も低いRMSE値になりました。
LigntGBMはうまく実行できるか不安でしたが、なんとか実装できました。

# 結果からの考察

株価予測の精度をさらに高めるためには、いくつかのアプローチがあります。
また、時系列データに特化したモデル、例えばLSTM（Long Short-Term Memory）などを使用することで、予測の精度をさらに向上させることができるかもしれません。
しかしながら、株価の分析には数字だけでなく、社会情勢や経済の状況など多面的な要素を考慮する必要があります。したがって、データ分析によって得られた予測が実際の株価予測に直接的に結びつくわけではないことを理解しておくことが重要です。

# 最後に

今回は、「データ分析の第一歩」として、基礎的な処理に取り組んでみました。Pythonや機械学習に関してプログラミング初心者の私でも、挫折することなく体系的に学ぶことができ、実践的なスキルを身につける機会を得られました。この経験を通じて、データ分析の世界が一気に広がったと感じています。

今後は、より専門的な分野である時系列解析や感情分析などに取り組み、さらに深い理解を得て、本格的な分析を実現できることを目指しています。この学びを活かして、より高度なデータ解析に挑戦していきたいと思っています。