- ロジスティック回帰の実装方法(ソースコードつき)
- ロジスティック回帰での株価予測の方法
ロジスティック回帰による株価予測の概要
予測は以下の流れで行います。
つまり,30日後に株価が上がるか下がるかを予測する分類モデルになります!
![](https://icochan1.net/wp-content/uploads/2023/02/5f63f49b6b7d98988e1002a4a4f2d6d5-3-1024x576.png)
他の手法でも予測をしているので,ぜひご覧ください!
![](https://icochan1.net/wp-content/uploads/2023/02/7d2a8a5d380f463388d1c8d4512bef57-2-300x169.png)
![](https://icochan1.net/wp-content/uploads/2023/02/40dcbe9d88c96297b642179a653642d8-1-300x169.png)
![](https://icochan1.net/wp-content/uploads/2023/02/9d831d187199270f191b3999ca596311-1-300x169.png)
![](https://icochan1.net/wp-content/uploads/2023/02/60275ab58ebd7cd921d15fb49998721b-1-300x169.png)
事前準備
まずは,環境についてです。
- Python 3.8.15
- macOS Monterey 12.5
必要なライブラリは以下のようになります。ない場合にはpip
やconda
で入れてください。
必要なライブラリ
- pandas
- numpy
- sklearn
- datetime
- pandas_datareader
ライブラリのインポート
データを扱うのに必要なライブラリのインポートを行います。
import pandas as pd
import numpy as np
from datetime import datetime
株価情報の取得
主要な株価指数の取得
予測のための説明変数として,日経平均株価などを取得します。
今回は,日経225,S&P500,ナスダック総合指数を取得しています。
from pandas_datareader import data
marketData = ['NIKKEI225','SP500','NASDAQCOM']
df_market = data.DataReader(marketData,'fred','2014-01-01', '2023-1-31').asfreq("D")
df_market = df_market.fillna(method='ffill')
df_market = df_market.dropna(how='any')
3行目:df_market = data.DataReader…
指数を2014年1月1日から2023年1月31日まで取得しています。
4行目:df_market = df_market.fillna…
欠損値を埋めています。
5行目:df_market = df_market.dropna…
欠損値がある場所は削除しています。
株価取得の詳しい内容については以下をご覧ください。
![](https://icochan1.net/wp-content/uploads/2022/09/0ae7c62e50dc705df75843b104ce66cd-1-300x169.jpg)
主要な株価指数の前処理
株価指数そのままでは学習しにくいので,移動平均との乖離を計算します。
ここは精度に関係するので試行錯誤する必要があります。
# 各指数の移動平均との乖離率を算出する
for index_name in df_market.columns:
for i in [5,10,30,60,90,120]:
df_market[index_name + "_" + str(i) + "days_diffrol"] =\
(df_market[index_name].rolling(i).mean() - df_market[index_name]) / df_market[index_name].rolling(i).mean()
# 生データは削除する
df_market = df_market.drop(columns=index_name)
予測する企業の株価を取得
予測したい企業の株価を取得します。
CODE=
の部分は予測したい企業の株価コードを指定します。
今回は4755
の企業の予測をしています。
# 予測する企業の株価データを取得
from pandas_datareader.stooq import StooqDailyReader
CODE = 4755
CODE_str = str(CODE) + ".JP"
start = datetime(2014, 1, 1)
end = datetime(2023, 1, 31)
df_target = StooqDailyReader(CODE_str, start=start, end=end).read()
df_target = df_target.sort_values('Date')
df_target = pd.DataFrame(df_target).asfreq("D", method="ffill")
5行目:start = datetime(2014, 1, 1)
取得する範囲のスタートを設定します。end
も同じです。
8行目:df_target = StooqDailyReader…
データを取得します。
9行目:df_target = df_target.sort_values(‘Date’)
df_target
には最近のデータから順番に入っているので昇順にします。
10行目:df_target = pd.DataFrame…
データの欠損値処理をしています。
予測対象の作成
予測するのは,30日後に株価が上がるか下がるかです。
今回は,今日の終値と30日後の終値を比較して10%以上増加するかしないかを設定しています。
分類問題なので,増加する場合に「1」,それ以外は「0」としています。
# 目的変数の作成(30日後に5 %以上上昇する時に1)
df_target["30days_after"] = df_target["Close"].shift(-30)
df_target['ratio'] = df_target['30days_after'] / df_target["Close"]
df_target['target'] = np.where(df_target['ratio'] > 1.1, 1, 0)
# 不要な行と列を削除
df_target = df_target.dropna(how='any')
df_target = df_target.drop(columns=["ratio","30days_after"])
2行目:df_target[“30days_after”] = …
Close
に入っている終値を30日ずらすことで,30日後の終値を取得しています。
3行目:df_target[‘ratio’] = …
30日後の終値を現在の終値で割ることで比を計算しています。
4行目:df_target[‘target’] = …
前述で計算した比率が10%以上増加している場合に「1」,それ以外に「0」をtargetに保存しています。
データの整理
データの結合
これまでに作成したデータを結合して1つのdataframeにします。
# データの結合
df = pd.merge(df_target, df_market, how='left', left_index=True, right_index=True)
df = df.dropna(how='any')
学習データとテストデータに分割
今回は,2021年までのデータを使って2022年の予測を行います。
# トレインとテストに分割
df_train = df[:"2021"]
df_test = df["2022":]
# 目的変数と説明変数に分割
X_train = df_train.drop("target", axis=1)
y_train = df_train["target"]
X_test = df_test.drop("target", axis=1)
y_test = df_test["target"]
モデルの作成
ロジスティック回帰での学習
いよいよ作成したデータを学習させます。
# 学習
from sklearn.linear_model import LogisticRegression
# ロジスティック回帰のインスタンス
model = LogisticRegression(penalty='l2', # 正則化項(L1正則化 or L2正則化が選択可能)
dual=False, # Dual or primal
tol=0.0001, # 計算を停止するための基準値
C=1.0, # 正則化の強さ
fit_intercept=True, # バイアス項の計算要否
intercept_scaling=1, # solver=‘liblinear’の際に有効なスケーリング基準値
class_weight=None, # クラスに付与された重み
random_state=None, # 乱数シード
solver='lbfgs', # ハイパーパラメータ探索アルゴリズム
max_iter=100, # 最大イテレーション数
multi_class='auto', # クラスラベルの分類問題(2値問題の場合'auto'を指定)
verbose=0, # liblinearおよびlbfgsがsolverに指定されている場合、冗長性のためにverboseを任意の正の数に設定
warm_start=False, # Trueの場合、モデル学習の初期化に前の呼出情報を利用
n_jobs=None, # 学習時に並列して動かすスレッドの数
l1_ratio=None # L1/L2正則化比率(penaltyでElastic Netを指定した場合のみ)
)
model.fit(X_train, y_train) # モデルの学習
3行目:model = LogisticRegression…
ロジスティック回帰の設定を定義しています。
21行目:model.fit(X_train, y_train)
学習データを入れて学習させます。
結果の保存
予測結果を保存して精度を確認します。
df_result
に結果をまとめます。
# 予測値と実値との比較
df_result = pd.DataFrame()
df_result["Price"] = df_target["Close"]["2022-01-01":]
df_result["true"] = y_test
df_result["Pred"] = model.predict(X_test)
5行目:df_result[“Pred”] = model.predict(X_test)
テストデータをモデルに入れた時の結果を保存しています。
結果の確認
混同行列で結果を確認してみましょう。
![](https://icochan1.net/wp-content/uploads/2023/02/ee3be1d100caf1aa771552d59c2bd436-2-1024x576.png)
どれだけ正しく分類できたかを表すAccuracyを計算すると,344/366で94%となりました。
数値だけ見ると良い数字ですが,全てを「上昇しない」と予測するモデルとなってしまいました。
ですので,使えるモデルにするためにはパラメータチューニングを行う必要があります。
他の精度を上げる方法は以下の方法が考えられます。
精度を上げる方法
- 他のアルゴリズムを使用する
- パラメータチューニングをする
- 他の指標を使って予測する
機械学習を使ったモデルは以下の記事で紹介しています!
![](https://icochan1.net/wp-content/uploads/2023/02/40dcbe9d88c96297b642179a653642d8-1-300x169.png)
ソースコードまとめ
今回使用したコードはこちらになります。
import pandas as pd
import numpy as np
from datetime import datetime
# 市場データの取得
from pandas_datareader import data
marketData=['NIKKEI225','SP500','NASDAQCOM']
df_market = data.DataReader(marketData,'fred','2014-01-01', '2023-1-31').asfreq("D")
df_market = df_market.fillna(method='ffill')
df_market = df_market.dropna(how='any')
# 各指数の移動平均との乖離率を算出する
for index_name in df_market.columns:
for i in [5,10,30,60,90,120]:
df_market[index_name + "_" + str(i) + "days_diffrol"] =\
(df_market[index_name].rolling(i).mean() - df_market[index_name]) / df_market[index_name].rolling(i).mean()
# 生データは削除する
df_market = df_market.drop(columns=index_name)
# 予測する企業の株価データを取得
from pandas_datareader.stooq import StooqDailyReader
CODE = 4755
CODE_str = str(CODE) + ".JP"
start = datetime(2014, 1, 1)
end = datetime(2023, 1, 31)
df_target = StooqDailyReader(CODE_str, start=start, end=end).read()
df_target = df_target.sort_values('Date')
df_target = pd.DataFrame(df_target).asfreq("D", method="ffill")
# 目的変数の作成(30日後に5 %以上上昇する時に1)
df_target["30days_after"] = df_target["Close"].shift(-30)
df_target['ratio'] = df_target['30days_after'] / df_target["Close"]
df_target['target'] = np.where(df_target['ratio'] > 1.1, 1, 0)
# 不要な行と列を削除
df_target = df_target.dropna(how='any')
df_target = df_target.drop(columns=["ratio","30days_after"])
# データの結合
df = pd.merge(df_target, df_market, how='left', left_index=True, right_index=True)
df = df.dropna(how='any')
# トレインとテストに分割
df_train = df[:"2021"]
df_test = df["2022":]
# 目的変数と説明変数に分割
X_train = df_train.drop("target", axis=1)
y_train = df_train["target"]
X_test = df_test.drop("target", axis=1)
y_test = df_test["target"]
# 学習
from sklearn.linear_model import LogisticRegression
# ロジスティック回帰のインスタンス
model = LogisticRegression(penalty='l2', # 正則化項(L1正則化 or L2正則化が選択可能)
dual=False, # Dual or primal
tol=0.0001, # 計算を停止するための基準値
C=1.0, # 正則化の強さ
fit_intercept=True, # バイアス項の計算要否
intercept_scaling=1, # solver=‘liblinear’の際に有効なスケーリング基準値
class_weight=None, # クラスに付与された重み
random_state=None, # 乱数シード
solver='lbfgs', # ハイパーパラメータ探索アルゴリズム
max_iter=100, # 最大イテレーション数
multi_class='auto', # クラスラベルの分類問題(2値問題の場合'auto'を指定)
verbose=0, # liblinearおよびlbfgsがsolverに指定されている場合、冗長性のためにverboseを任意の正の数に設定
warm_start=False, # Trueの場合、モデル学習の初期化に前の呼出情報を利用
n_jobs=None, # 学習時に並列して動かすスレッドの数
l1_ratio=None # L1/L2正則化比率(penaltyでElastic Netを指定した場合のみ)
)
model.fit(X_train, y_train) # モデルの学習
# 予測値と実値との比較
df_result = pd.DataFrame()
df_result["Price"] = df_target["Close"]["2022-01-01":]
df_result["true"] = y_test
df_result["Pred"] = model.predict(X_test)
コメント