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(時系列の順番)も結構効いている気が。

f:id:iiiiikamirin:20211027231509p:plain