Python 株価データの欠損値をその前後の値で補完後、単純移動平均を算出する「pandas」

'20/05/16更新:移動平均計算を2つ→3つに増やし、計算によって生じた欠損値の行を削除してcsvファイルに出力する仕様に更新した。
 本記事では、下図のような単純移動平均のグラフと、そのデータをcsvファイルで保存する雛形コードを載せています。

f:id:HK29:20200516085140p:plain

●株価データの入手手順

 例えば、ヤフーファイナンスで入手できます。https://finance.yahoo.com/quote/%5EN225/history?p=^N225

 末尾のN225日経平均株価東証一部上場の代表225銘柄の平均株価)のことです。上記リンクをクリックすると、下図のような表が表示されます。

f:id:HK29:20200510172952p:plain

 図中の「Time Period」で抽出区間を指定し、「Apply」で適用します。その後、その下の「Download」をクリックすればcsvファイル形式で保存できます。

●本プログラム実行によるデータ処理の流れ

 上記で入手したcsvファイルは下図のように欠損値nullが存在します。

f:id:HK29:20200510174427p:plain

 下図のように、それら欠損値をその前後の値で補完します。pandasのdf.interpolate()だけで処理できます。

f:id:HK29:20200510174732p:plain

 但し、もし、データの上端が欠損値であった場合に、その1行だけは欠損として残ってしまうため、万が一のために除去する操作を挟んでおきます。df.dropna()だけで欠損行を全て削除できます。その後単純移動平均を算出して列に追記します。

f:id:HK29:20200510175736p:plain

 最後に、単純移動平均の算出によって生じたデータの先頭行の欠損行を削除してcsvファイルへ出力します。

f:id:HK29:20200516084910p:plain

■本プログラム

#!/usr/bin/env python3
# coding: utf-8
# "https://finance.yahoo.com/quote/%5EN225/history?p=^N225"
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import japanize_matplotlib

def main():
    df = pd.read_csv(csv_path) # pandasのDataFrame形式で読み込む

    df_i = df.interpolate() # 欠損値を前後の値で補完する
    df_i.to_csv(file_name + '_01_interpolate.csv', sep=',', index=False, encoding='utf-8')

    df_d = df_i.dropna() # もし、上端の行データが欠損値だった場合は残るので削除する
    df_d.to_csv(file_name + '_02_dropna.csv', sep=',', index=False, encoding='utf-8')

    df_s = df_d.sort_values(date_column, ascending=True) # (念のため)日付を昇順に並び替え操作
    x = df_s[date_column] # グラフ化する時のX軸をpandasで読み出す(一列なのでSeries形式) 
    y = df_s[target_column]
    
    # 単純移動平均の計算
    simple_moving_average_1 = pd.Series.rolling(y, window=moving_average_1).mean()
    simple_moving_average_2 = pd.Series.rolling(y, window=moving_average_2).mean()
    simple_moving_average_3 = pd.Series.rolling(y, window=moving_average_3).mean()

    # グラフオブジェクトを作成
    plt.figure(figsize=(15,6))
    plt.style.use('dark_background') # グラフの見栄え 'bmh' 'dark_background' 'fivethirtyeight'
    ax = plt.gca() # 現在のfigureをAxesオブジェクト変数化
    
    # x, yをプロット
    ax.plot(x, y, color="w", lw=1, label="Close")
    ax.plot(x, simple_moving_average_1, color="r", label="移動平均 {} 日".format(moving_average_1))
    ax.plot(x, simple_moving_average_2, color="cyan", label="移動平均 {} 日".format(moving_average_2))
    ax.plot(x, simple_moving_average_3, color="lime", label="移動平均 {} 日".format(moving_average_3))
    ax.set_xlabel('日付') # X軸名
    ax.set_ylabel('株価') # Y軸名
    plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=16)
    
    # X軸目盛表記を調整する
    x_ticklabels = ax.get_xticklabels() # デフォルトの目盛り表記をゲットする
    plt.setp(x_ticklabels, rotation=75) # 目盛り表記を90度回転。#フォントサイズの指定する場合 ,fontsize=16)
    tick_spacing = 30 # 目盛り表示する間隔
    ax.xaxis.set_major_locator(ticker.MultipleLocator(tick_spacing)) # X軸目盛の表示間隔を間引く
    
    ax.grid(True) # 格子を入れる
    plt.rcParams['font.size'] = 18 # ラベルやタイトルなど全フォントのデフォルトサイズ
    plt.subplots_adjust(right=0.7) # 右端の余白
    plt.tight_layout() # ラベルレイアウトを調整する。文字がはみ出ないようにする。
    plt.savefig(file_name + '.png')
    plt.close()
    
    ### pandasを列方向結合する
    # Series形式同士を結合する場合
    df_new = pd.concat((simple_moving_average_1.rename('moving_average_' + str(moving_average_1)),
                        simple_moving_average_2.rename('moving_average_' + str(moving_average_2)),
                        simple_moving_average_3.rename('moving_average_' + str(moving_average_3))),
                        axis=1, sort=False)
    # DateFrame形式同士を結合する場合、リストで与える
    DF = pd.concat([df_s, df_new], axis=1)
    
    # csvファイルを作成する
    DF.to_csv(file_name + '_03_after_preprocessing.csv', sep=',', index=False, encoding='utf-8')

    DF_d = DF.dropna() # 欠損値のある行を除去する
    DF_d.to_csv(file_name + '_04_done.csv', sep=',', index=False, encoding='utf-8')

if __name__ == '__main__':
    csv_path = 'N225_10years.csv'
    file_name = csv_path[:-4]
    date_column = 'Date'
    target_column = 'Close'
    moving_average_1 = 5
    moving_average_2 = 25
    moving_average_3 = 75

    main()

以上

<広告>