optiver-realized-volatility-prediction 3日目
目標がないと頑張れないので、今日の時点で銅メダルラインの0.1955を一旦に目標してみよう。方法はディスカッションやVote多いNotebookみてひたすらぱくる。
今日のまとめ
- スコア0.2192 -> 0.2134
今日の目標
前回うまくいったボラのラグを取る特徴量を他の値でも試す
クラスタリングしてその平均取るやつをやる。
前回うまくいったボラのラグを取る特徴量を他の値でも試す
realized_volを正しいtime_idに並べ替えて、そのラグをとったらスコア結構あがった。前回はラグ1しかとらなかったので、それを1,2,5...のように複数取るだけ。
クラスタリングしてその平均取るやつをやる。
time_idとstock_idで類似性が高いものをクラスタリングして、その平均取った値を特徴量にする。
サンプルコード
target_feature = 'book.log_return1.realized_volatility' n_max = 40 # make neighbors pivot = df.pivot('time_id', 'stock_id', 'price') pivot = pivot.fillna(pivot.mean()) pivot = pd.DataFrame(minmax_scale(pivot)) nn = NearestNeighbors(n_neighbors=n_max, p=1) nn.fit(pivot) neighbors = nn.kneighbors(pivot) # aggregate def make_nn_feature(df, neighbors, f_col, n=5, agg=np.mean, postfix=''): pivot_aggs = pd.DataFrame(agg(neighbors[1:n,:,:], axis=0), columns=feature_pivot.columns, index=feature_pivot.index) dst = pivot_aggs.unstack().reset_index() dst.columns = ['stock_id', 'time_id', f'{f_col}_cluster{n}{postfix}_{agg.__name__}'] return dst feature_pivot = df.pivot('time_id', 'stock_id', target_feature) feature_pivot = feature_pivot.fillna(feature_pivot.mean()) neighbor_features = np.zeros((n_max, *feature_pivot.shape)) for i in range(n): neighbor_features[i, :, :] += feature_pivot.values[neighbors[:, i], :] for n in [2, 3, 5, 10, 20, 40]: dst = make_nn_feature(df, neighbors, feature_pivot, n) df = pd.merge(df, dst, on=['stock_id', 'time_id'], how='left')
実装コード
中に入れ子で関数作ったりして、変数の受け渡しがめんどくさかったのでclassつかってみた。いまいちうまく使えているかわからん。。
from sklearn.neighbors import NearestNeighbors class NearestNeiborFeature: def __init__(self, f_nn, f_agg, type_): self.n_max = 40 self.feature_nn = f_nn self.feature_agg = f_agg self.type_ = type_ def nn_features(self, df): ''' 特徴量Xのminmax_scaleで近いもの同士(time_id, stock_id)を集めて、その平均を取る - time_idのとき time_idが近い=そのときfeature_nnのminmax水準が近い 各stockに対してtimeが近いn個のfeature_aggを抽出して、その平均取る ''' def make_nn_feature(df, neighbors, n, agg=np.mean): pivot_aggs = pd.DataFrame(agg(neighbors[1:n,:,:], axis=0), columns=df.columns, index=df.index) dst = pivot_aggs.stack().reset_index() dst.columns = ['time_id', 'stock_id', f'{self.type_}_cluster{n}_{agg.__name__}_{self.feature_nn}'] return dst # make neighbors pivot = df.pivot('time_id', 'stock_id', self.feature_nn) pivot = pivot.fillna(pivot.mean()) pivot = pd.DataFrame(minmax_scale(pivot)) if self.type_ == "stock": pivot = pivot.T # stockのときのみ転置 nn = NearestNeighbors(n_neighbors=self.n_max, p=1) ''' n_neibors: 近いとして持ってくる個数 p(int), default=2: 距離の出し方 Parameter for the Minkowski metric from sklearn.metrics.pairwise.pairwise_distances. When p = 1, this is equivalent to using manhattan_distance (l1), and euclidean_distance (l2) for p = 2. For arbitrary p, minkowski_distance (l_p) is used. ''' nn.fit(pivot) neighbors = nn.kneighbors(pivot) # tuple(0: distance, 1: index) # nnで近い順にn個取得、その平均 feature_pivot = df.pivot('time_id', 'stock_id', self.feature_agg) feature_pivot = feature_pivot.fillna(feature_pivot.mean()) if self.type_ == "stock": feature_pivot = feature_pivot.T # stockのときのみ転置 neighbor_features = np.zeros((self.n_max, *feature_pivot.shape)) for i in range(self.n_max): # 1d: nn, 2d: time_id, 3d: stock_id neighbor_features[i, :, :] += feature_pivot.values[neighbors[1][:, i], :] for n in [2, 3, 5, 10, 20, 40]: dst = make_nn_feature(feature_pivot, neighbor_features, n) df = pd.merge(df, dst, on=['stock_id', 'time_id'], how='left') return df
作成した特徴量
- stock_cluster5_mean_log_return_realized_volatility: クラスタリングしてその平均取ったりしたやつ
importance
地味にorder(時系列の順番)も結構効いている気が。