このプログラムの目的
これまでのプログラムで各銘柄についての学習データを生成できました.
このプログラムではすべての銘柄の学習データを縦方向に結合することです.
また,時系列を扱うにあたり,-n時間までの過去の特徴量も学習データとして追加します.
work_share
├02_get_stock_price
├Dockerfile
├docker-compose.yml
└src
├dataset_2018-2023
| ├clusterling_dataset_dist.dfpkl
| ├clusterling_dataset_mean.dfpkl
| ├original_dataset.dfpkl
| └learning_dataset.dfpkl (自動生成)
├original_data_2018-01-01_2023-01-01
| ├1301.csv
| ├1305.csv
| └...
├time_cluster_result
├get_stock_price.py
├make_dataset.py (これを作成)
├make_original_data.py
├make_time_cluster_dataset.py
└stocks_code.xls
使用ライブラリ
import pandas as pd
import numpy as np
import tqdm
import glob
import os
from sklearn.preprocessing import StandardScaler
元データのロード
original_dataset.dfpkl
,clusterling_dataset_mean.dfpkl
,clusterling_dataset_dist.dfpkl
をロードします.
このときoriginal_dataset.dfpklはStandardScaler
で正規化します.データの結合の前に正規化をしないと,銘柄による価格差が正規化の値に現れてしまいます.また,利益計算などに元の価格も必要なので,original_dfとして保持しておきます.
さらに,学習時に学習データとテストデータを分けやすいようにeval
としてtrain
なのかtest
なのかを記録しておきます.
def df_split(df, train_rate):
train_num = int(len(df) * train_rate)
train_df = df.iloc[:train_num, :].reset_index(drop=True)
test_df = df.iloc[train_num:, :].reset_index(drop=True)
return train_df, test_df
def make_dataset(args):
original_df = pd.read_pickle(args['original_dataset_path'])
class_mean_df = pd.read_pickle(args['class_mean_dataset_path'])
class_dist_df = pd.read_pickle(args['class_dist_dataset_path'])
print(args['drop_codes'])
original_df = original_df.drop(args['drop_codes'], axis=1)
scaler_df = original_df.copy()
scaler_df = scaler_df.reset_index(drop=True)
train_df, _ = df_split(scaler_df, args['train_rate'])
scaler = StandardScaler()
scaler.fit(train_df)
scaler_df = pd.DataFrame(data=scaler.transform(scaler_df), columns=scaler_df.columns)
scaler_df['eval'] = 'test'
train_num = int(len(scaler_df) * args['train_rate'])
scaler_df.loc[:train_num, 'eval'] = 'train'
dfs = {
'original_df':original_df,
'scaler_df':scaler_df,
'class_mean_df':class_mean_df,
'class_dist_df':class_dist_df
}
save_dataset(dfs, args)
学習データセットの作成と保存
ロードしたデータを基に学習データセットを作成します.
また,各時系列データにおけるtime_length
の長さまでのt0, t1, …, tNを特徴量として保存します.
def save_dataset(dfs, args):
original_df = dfs['original_df']
scaler_df = dfs['scaler_df']
class_mean_df = dfs['class_mean_df']
class_dist_df = dfs['class_dist_df']
original_df = original_df.reset_index()
next_original_df = original_df.shift(-1).reset_index(drop=True)
date = pd.to_datetime(original_df['Date'])
original_df = original_df.drop('Date', axis=1)
df_list = []
for stock_code in tqdm.tqdm(original_df.columns):
df = pd.DataFrame({
'date':date,
'eval':scaler_df['eval'],
'original_value':original_df[stock_code],
'next_original_value':next_original_df[stock_code],
#'value':original_df[stock_code],
})
Xt = pd.DataFrame({
f'value_t{i}':scaler_df[stock_code].shift(i).values for i in range(args['time_length']+1)
})
df = pd.concat([df, Xt], axis=1)
for col in class_mean_df.columns:
temp_Xt = pd.DataFrame({
f'{col}_t{i}':class_mean_df[col].shift(i).values for i in range(args['time_length']+1)
})
df = pd.concat([df, temp_Xt], axis=1)
for class_name, class_dist in class_dist_df[stock_code].to_dict().items():
df[f'{class_name} dist'] = class_dist
df['code'] = stock_code
df = df.loc[args['time_length']+1:, :].reset_index(drop=True)
nan_mask = df['next_original_value'].isna()
df = df.loc[nan_mask != True, :].reset_index(drop=True)
df_list.append(df)
out_df = pd.concat(df_list)
out_df = out_df.sort_values('date')
out_df.to_pickle(args['out_path'])
print(out_df)
パラメータと実行
time_length
は30としていますが,それに応じて生成されるデータセットのサイズが変動します.より多くの過去特徴を使用したい場合は増やし,データセットのサイズを減らしたい場合は減らしてください.
if __name__ == '__main__':
dataset_dir = 'dataset_2018_2023'
args = {
'out_path':f'./{dataset_dir}/learning_dataset.dfpkl',
'original_dataset_path': f'./{dataset_dir}/original_dataset.dfpkl',
'class_mean_dataset_path': f'./{dataset_dir}/clusterling_dataset_mean.dfpkl',
'class_dist_dataset_path': f'./{dataset_dir}/clusterling_dataset_dist.dfpkl',
'time_length':30,
'train_rate':0.7,
'drop_codes':['2747', '3858', '6775', '9468']
}
make_dataset(args)