データの前処理方法
前回の投稿「1. 学習用データの集め方」にて、為替レート(Open, Close, High, Low, Volume)の時系列データがCSVファイル等で入手できていることとします。
このデータに対して、前処理を行います。
学習データの前処理は、分析対象によっては「前処理」だけで専門書が出版されるぐらい、とても大事な作業です。前処理のやり方によって、学習モデルの成否が大きく変わります。演算の誤りがないように、注意して行いましょう。
大きく分けて、3つの処理を実施します。
- データクレンジング
- 変化量へ変換
- スケーリング
プログラミング言語はPython3を用います。
NumPyやPandas等のライブラリを使用すると、楽にデータ処理を行えます。
例えばPandasの場合、CSVファイルの読み込みは下記ソースコードのように、簡単に行えます。
import pandas as pd
# 時系列データ(CSVファイル読み込み)
trade_df = pd.read_csv(filePath, index_col=False, usecols=['Time','Open','Close','High','Low','Volume'], encoding='UTF-16')
index_col, usecols, encoding は、読み込むCSVファイルの形式を確認し、適宜変更が必要です。
1. データクレンジング
不正データ(外れ値)や欠損データを補う処理を行います。
1.1. 外れ値の除去・補正
不正データ(外れ値)は、「2.変化量へ変換」のように、時系列データを変化量に変換後、
ヒストグラムにすると視覚的にわかりやすいです。外れ値があれば、下記図のようにわかるはずです。
Pythonに慣れている方は、Matplotlibライブラリ等を用いてご確認ください。
Excel等の表計算ソフトに慣れている人は、CSVファイルを直接開いて確認してもいいと思います。
このような外れ値を含んでいる場合、
quantile(分位数)を用いて外れ値を含むレコード(同時刻レコード)を検知して、
レコードごと(同じ時刻のOpen, Close, High, Low, Volumeを全て)除去するか、
他の値で置換します。
1.1.1. レコード除去の場合
学習用データが十分にある場合は、レコード除去が有効です。
正確なデータだけ使うようにしましょう。
Pandasを用いたソースコードの例は以下の通りです
# Open値ついて、95%の分位数よりも値が小さいデータを抽出してそれ以上の値を除外する
q = trade_df['Open'].quantile(0.95)
trade_df = trade_df[trade_df['Open'] < q]
1.1.2. 外れ値を置換の場合
学習用データが少なくレコード除去したくない場合は、置換もできます。
時系列データの場合は、前後の時刻データからある程度予測ができるからです。
例えば 時刻 t の間隔が短い場合、下記数式が成り立つはずです。
Open(t) ≒ Close(t-1)
正確には違いますが、もし Open 値が外れ値の場合、1時刻前の Close 値を用いても問題ないでしょう。
また、 High 値が外れ値の場合、下記数式で置き換えてもいいと思います。
High(t) ≒ \frac{High(t-1) + High(t+1)}{2}
こちらも正確には違いますが、外れ値が同時刻付近で頻発していない場合は、有効な手段かと思います。
Pandasを用いたソースコードの例は以下の通りです
q = trade_df['Open'].quantile(0.95)
outlier_df = trade_df[trade_df['Open'] < q]
outlier_list = outlier_df.index.tolist()
for index in outlier_list:
# 1列目:'Open', 2列目:'Close'の場合、Close(t-1)で置換する
trade_df.iloc[index, 1] = trade_df.iloc[index-1, 2]
Coffee Break
前回の投稿に記載した通り、一昔前(10年ほど前)は、外れ値を含む為替の時系列データがたくさんWeb上に公開されていました。昨今はこのようなデータは少なくなったと実感しています。この節の処理は今後、使うことはないでしょう。
1.2. 欠損データの除去・補正
不正データ(外れ値)は少なくなりましたが、欠損データはたびたびあります。
特に注意しないといけないのが、時間の欠損です。
次回の投稿で詳しく記述するつもりですが、
1つの通貨ペアの情報だけで予測を行うのは、情報量が少なすぎるため限界があります。
そのため複数の通貨ペアの情報や、相関のある市場データ(株価等)も組み合わせて学習データにします。
その際、時刻毎データを並べると、例えば下記の表のように、同一時刻で特定のデータが欠損していることが多々あります。特に週替わりにはたびたび発生します。
また、アメリカなどで行われているサマータイムにも注意してください。
市場の取引時間が変わるためです。
Index | Time | USDJPY | EURUSD | AUDJPY |
---|---|---|---|---|
1 | 2020/01/23 01:00 | 150.569 | 1.03749 | NaN |
2 | 2020/01/23 01:05 | 150.579 | 1.03584 | 93.472 |
3 | 2020/01/23 01:10 | 150.489 | 1.03798 | 93.480 |
4 | 2020/01/23 01:15 | 150.864 | 1.03445 | 93.570 |
5 | 2020/01/23 01:20 | 150.568 | 1.03545 | 93.611 |
: | : | : | : | : |
x | 2020/01/24 00:00 | 150.001 | NaN | NaN |
このような欠損データを含んでいる場合、上記「1.1. 外れ値の除去・補正」同様、
同時刻のレコードごと除去するか、1つ前の時刻レコードで置換します。
複数の時系列データの統合と、
欠損データを含むレコードを削除するときのソースコードの例は以下の通りです
USECOLS = ['Time','Open','Close','High','Low','Volume'] #CSVファイルの全要素
df_A = pd.read_csv(csv_file_A, usecols=USECOLS,
parse_dates= ['Time']) # 時刻タイプに変換
df_B = pd.read_csv(csv_file_B, usecols=USECOLS,
parse_dates= ['Time']) # 時刻タイプに変換
# 時系列データの統合(Time列を基準とする)
df_AB = pd.merge(df_A, df_B, on='Time', how='outer')
# 欠損データを1つでも含む場合、レコード削除
df_AB = df_AB.dropna(how='any')
この時間の欠損に対する処理は、細心の注意を払うことをオススメします。
過去に、ソースコードを誤って、欠損データを1時刻先の未来のデータで置換したことがあります。
その学習データを用いて学習モデルを生成したところ、未学習の(はずの)未来の為替の予測精度が80%以上と、爆発的に向上したことがありました。 当時は大いに喜んで「すごいものをつくってしまった」と興奮したのを覚えています。
また、間違いに気づくまでだいぶかかったため(数か月)、皆様は同じ間違いを起こさないよう、ご注意ください。
2. 変化量へ変換
入手した時系列データを、機械学習で学習させるため、定常化させます。
具体的には、差分をとります。
これもPandasを用いると、とても簡単に実行できます。
df_AB = df_AB.diff()
以上です。ただし、0レコード目はすべてNaNデータとなりますので、削除してください。
3. スケーリング
最後に、機械学習で学習しやすいようにするため、「2. 変化量への変換」後のデータに対して数値変換を行い、平均や分散、最小値・最大値を変更します。USDJPYの変動が大きく、EURUSDの変動が小さいような、要素ごとの偏りを無くす目的があります。
ここで、スケーリングに関する一般的な用語を定義します。
- 標準化:平均0,分散1へ変換
- 正規化:0~1へ変換
- 白色化:要素間の相関を無くす変換
詳しくは専門書籍や他のWebサイトをご参照ください。
ちなみに私は下記の書籍で学びました。数学寄りの専門書です。
どの手法を用いるのか、ここは個人の判断が分かれるところかと思います。
私の経験上、「1. データクレンジング」と「2. 変化量へ変換」まで完了したデータであれば、そこまで偏ったデータとはならず、通貨毎の特徴をなくしたくないと思い、あえて標準化のみを行っています。
(もしかしたら専門家の先生からしたら、それはダメだと言われるかもしれません)
標準化の数式は下記のとおりです。
平均:\bar{x} = \frac{1}{N} \sum_{n=1}^N x(n)
標準偏差:\sigma_{x} = \sqrt{\frac{1}{N} \sum_{n=1}^N (x(n) - \bar{x})^2}
x(t) ← \frac{x(t) - \bar{x}}{\sigma_{x}}
Pandasを用いたソースコードは下記のとおりです。
mean_val = df_AB .mean(axis=0) # 平均
df_AB = df_AB - mean_val
std_val = df_AB .std(axis=0) # 標準偏差
df_AB = df_AB / std_val
この変換後の時系列差分データをCSVファイル、またはNumpyファイルへ出力し、
以降の処理で用いています。
まとめ
学習用データの前処理方法について記載しました。
何度も記載していますが、この処理は丁寧に行い、計算式に誤りがないか何度も確認しましょう。誤ったデータで学習させるのは本当に無駄です。
また、専門書があるように、私が記載した内容はすべてではありません。
データ前処理の方法について、少しでも参考にしていただけますと幸いです。
次は、「学習に用いるデータの選定方法」について、投稿する予定です。
よろしくお願いいたします。