May 16, 2021

Определяем важные признаки по аномальным целевым значениям

Рассмотрим один из подходов для определения важных для модели признаков по всплескам значений таргета.

Предположим в распоряжении имеется набор данных следующего вида (код представлен в конце статьи):

Как можно убедиться, величина цели специально взята как квадрат номера месяца. Допустим, мы исследуем влияние month на target. Это делается разными способами, о наиболее распространенных из которых я писал ранее.

Кроме того, можно просто посчитать статистику значений признаков, при которых достигается максимальная или минимальная величина таргета. Особый вес такой подход приобретает при анализе временных рядов, когда, по сути, время является важнейшим показателем при прогнозировании цели.

Также помимо достижения экстремальных значений нас могут интересовать перепады в величине таргета. Эти способы я объединил в следующую функцию:

def get_anomal_feat(df, feat_col, target_col, num_anom, low_high='low', diffs=True, sort_col=False):
    if diffs:
        if sort_col:
            df.sort_values(by=sort_col, inplace=True)
        anoms_ar = np.diff(df[target_col])
        idx_add = 1
    else:
        anoms_ar = df[target_col].values
        idx_add = 0
    if low_high=='low':
        idx = np.argsort(anoms_ar)[:num_anom]
    if low_high=='high':
        idx = np.argsort(anoms_ar)[-num_anom:]

    return df[feat_col].iloc[idx_add+idx]

На вход функции мы задаем матрицу признаков, название столбца с анализируемыми категориальными значениями, имя колонки с целью, количество значений признаков, направление (минимум-максимум/low-high), флаг перепада значений-абсолютных значений и для перепадов имя столбца сортировки матрицы.

Например посчитаем 8 значений месяца, соответствующих максимальным target в таблице:

data_new = get_anomal_feat(data, 'month', 'target', 8, 'high', diffs=False)
np.bincount(data_new)
data_new.value_counts()

Аналогичную статистику можно считать как для всей матрицы, так и для каждого объекта в отдельности, если полагаем, что влияние признака на таргет сильно меняется от этого. Выведем по два месяца с наименьшими значениями в перепадах target для каждого объекта:

data_new = data.groupby('obj').apply(lambda df: get_anomal_feat(df, 'month', 'target', 2, 'low', diffs=True, sort_col='feat'))
np.bincount(data_new)
data_new.value_counts()

Ниже представлен код для генерации использованного выше набора данных:

import numpy as np
import pandas as pd

np.random.seed(0)
obj = np.arange(1,4)
# допустим это месяцы за два года
# feat = np.tile(np.arange(1,13),2)
feat = pd.period_range(start='2018-01', end='2019-12', freq='M')
# матрица для каждого объекта 24 месяца
data = pd.MultiIndex.from_product([obj,feat]).to_frame().reset_index(drop=True)
data.columns = ['obj', 'feat']
data['month'] = data['feat'].map(lambda x: x.month)
# в качестве target возьме условно значения, чтобы проследивалась связь с месяцем
data['target'] = np.power(data['month'],2)
data = data.sample(frac=1)