初心者がゆく...回帰分析を用いた株価予測


## はじめに

※このブログはAidemy Premiumのカリキュラムの一環で、受講修了条件を満たすために公開しています。

#### ◇自己紹介
　とある商社にてサプライチェーンマネジメントを行う部門に所属しており、
　昨今より存在感と重要度を増しているデータ分析・機械学習およびPythonについて理解を深め、
 　また業務に活かすことを目的としAidemy Premium「データ分析コース」を３ヶ月受講いたしました。

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

#### ◇実行環境
- **Google Colaboratory** (Google Colab)
- **Python 3.10.12**
　GoogleColab上でPythonのバージョンを調べる際は以下コードで確認できるみたいですね。(そのレベルから恐る恐るやらせていただいております)
```python:バージョン確認コード
$ python --version
Python 3.10.12
```

※Google Colabの環境整備についてはなんとか自力で対応できましたが、Aidemy会員はAidemy様にご相談に乗っていただけるそうでした。
 ちなみに以下の記事も参考になりそうです。
[【徹底解説】初心者のためのGoogle Colaboratoryの使い方](https://qiita.com/sitonoy/items/5009ca72e617364102df)

---

## **作成プログラム**

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

### ■ 概要
###### 分析内容：Apple社の株価データを用いて、ある日の終値を予想する分析を行なう
簡単に取得できるような株価データを用いて、**回帰分析**を行っていきます。
最終的にその精度も出せればと思っています。

### ■ 早速、やってみよう！
#### ...の前に、分析の流れを確認します。
- (1)データの準備
　　 ⇒今回はApple社の株価データを使わせていただきます。
   　　　何とかして、データをcsvで持ってきます。
- (2)データの整備
　　 ⇒持ってきたデータを、使える形に加工したり、不要部分を削除したりします。
- (3)データの分析
　　 ⇒必要データに絞った状態で、
   　　　pythonに用意されているモジュールを用いて、実際に分析を回してみます。
- (4)精度を確認する
　　 ⇒精度を評価します。

このような流れで対応したいと思います。

---
#### (1)データの準備

※はじめはド文系人間らしく、[Yahoo! Finance](https://finance.yahoo.com/quote/AAPL/history?p=AAPL)から直接「2022/11/01~2023/11/01」までのCSVデータをDL⇒Google CorabにそのCSVをアップロードしていましたが、明らかに以下の方法が楽だったので変更しました。

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

を用いて、以下のコードで直接取得することが可能です。

```python:yahooファイナンスから株価を取得するコード
#必要ライブラリのインポート
from pandas_datareader import data as pdr 
import yfinance as yf
yf.pdr_override()

#変数dataにApple社('AAPL')の株価データを代入。期間はstart,endで指定
data = pdr.get_data_yahoo('AAPL', start='2022-11-01', end='2023-11-01')
data.head() #データ内容の確認
```
慎重派なのですぐdata.head()でデータの内容を確認したくなります。
その内容は以下になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3599670/e0861063-d4b3-25e3-35cf-5ee63f89317e.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(10)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3599670/24d0c415-74d9-7913-5b85-0e1bff71c4e6.png)


・・・よさげですね。
あとは先頭の6行と、最終行1行に欠損値が出るので、削除しておきます。

```python:Nan(欠損値)削除
data_chart = data_chart.dropna()
#最初5行と最後5行を表示させる
data_chart
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3599670/b2e346e3-dac1-fed4-754c-88feebe73a09.png)

これで無事、2022-11-09から2023-10-30までの、欠損値のないデータとなりました。

#### (3)データの分析
pythonに用意されているモジュールを用いて、実際に動かしてみます。

```python:一旦LinearRegressionやってみます
# 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)

#線形回帰をモデルとして構築
model = LinearRegression()
#訓練データを読み込み
model.fit(x_train, y_train)
#x_testデータを使ってy(ラベル)の方を予測をさせます
pred_y = model.predict(x_test)
# 予測結果を出力
#print(pred_y)
```
#### (4)精度を確認する

重回帰分析のお作法通り、**決定係数R^2**で精度を確認したいと思います。

・**決定係数R^2**
は、正解データ内のばらつきに対して予測値のズレが小さいほど値が大きくなります。
決定係数は1以下の値を取り、おおよそ0.8以上の数値であれば精度よく予測ができていると言えるそうです。

```python:精度確認
#model.score()で決定係数を出力
print("決定係数は：" , model.score(x_test, y_test) ,"です")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3599670/12f3da2d-a607-e287-ef50-d9a4c106e534.png)



ここで、いま「訓練データ」と「検証データ」を8:2にしておりましたが、
試みに7:3の割合に変更してみたところ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3599670/d241e018-ce75-110e-3764-ef7a37c14471.png)

非常によくなりました。これでよさそうですね。
…

### **結果...の前に**
「訓練データを減らしたのに精度がよくなるんだな～」と思ったところで、逆にかつ突然に、**過学習**の可能性に気付きました。

pythonには簡単に**正則化**を行なったうえで重回帰分析を回してくれるようなモジュールも備えてくれているので、念のためひととおり行なってみます。

※そのあたりの意味や、各モデルが何を意味するかは、ここでは割愛します...

```python:どのモデルが最も良いか判定させるコード
#各種必要モジュールをインポート(追加分のみ)
from sklearn.linear_model import Lasso
from sklearn.linear_model import Ridge
from sklearn.linear_model import ElasticNet

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

# 線形回帰---------
model_Linear = LinearRegression()
model_Linear.fit(x_train,y_train)
  # 決定係数を出力
print("LinearRegression:{}".format(model_Linear.score(x_test, y_test)))

  # 先頭なのでそのまま値を入れる
UseModelScore = model_Linear.score(x_test, y_test)
UseModelName  = "LinearRegression"

# ラッソ回帰---------
model_Lasso = Lasso()
model_Lasso.fit(x_train,y_train)
  # 決定係数を出力
print("Lasso:{}".format(model_Lasso.score(x_test, y_test)))

  #　UseModelScoreと比較し、スコアが高ければ上書き
if UseModelScore < model_Lasso.score(x_test, y_test):
    UseModelScore = model_Lasso.score(x_test, y_test)
    UseModelName  = "Lasso"


# リッジ回帰---------
model_Ridge = Ridge()
model_Ridge.fit(x_train,y_train)
  # 決定係数を出力
print("Ridge:{}".format(model_Ridge.score(x_test, y_test)))

  #　UseModelScoreと比較し、スコアが高ければ上書き
if UseModelScore < model_Ridge.score(x_test, y_test):
    UseModelScore = model_Ridge.score(x_test, y_test)
    UseModelName  = "Ridge"

# ElasticNet回帰---------
model_ElasticNet = ElasticNet()
model_ElasticNet.fit(x_train,y_train)
  # 決定係数を出力
print("ElasticNet:{}".format(model_ElasticNet.score(x_test, y_test)))

  #　UseModelScoreと比較し、スコアが高ければ上書き
if UseModelScore < model_ElasticNet.score(x_test, y_test):
    UseModelScore = model_ElasticNet.score(x_test, y_test)
    UseModelName  = "ElasticNet"

print("最もスコアの高かったモデルは {0} で、その値は {1} でした".format(UseModelName,UseModelScore))
```

・訓練データ：検証データ = 8 : 2　での結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3599670/25ee8133-cde8-2525-5ba5-aaf24ef034f3.png)


・訓練データ：検証データ = 7 : 3　での結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3599670/acdfd5bd-fe14-602a-5a71-8495e8abfac8.png)

### 結論
Apple社の株価データの一部と「直前1週間の出来高合計」を変数として重回帰分析を行った場合、Lasso回帰を用いるのが最も良さそうです。

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

---
## **今後の活用**
今回はまず"データ分析の第一歩"というところで簡単な処理をさせていただきました。
 
Pythonや機械学習について体系立てて学べる有難い機会をいただいたことで、
今後は例えば自業務においてはご注文データや当社発注データを用いた時系列分析・需要予測、はたまた商品カテゴリーの適正化を目的として自然言語処理・分類化ができるな～等、世界が一気に広がりました。

## **おわりに**
　様々な場面でデータ活用の重要性が叫ばれているこのご時世において活用の方法の第一歩が分かったことは非常に大きかったので、Aidemy社には感謝の念が尽きません。
 今まで点と点でしかなかった知識や、「どこかで聞いたことあるけど...なんだっけ?」みたいなことが一気に繋がり、自信につながりました。
　学んだ内容を最大限社会へ還元できるよう、一層頑張ります。

#### 参考サイト様
- 学習サイト
・[Aidemy](https://aidemy.net/grit/premium/?utm_source=yahoo&utm_medium=cpc&utm_campaign=brand_brand_PPLP)

- Qiita自体について
・[Qiita記事の書き方](https://qiita.com/ToppaD/items/035cf5c42e94b92f7526)
・[Markdown記法 チートシート](https://qiita.com/Qiita/items/c686397e4a0f4f11683d)