2 способа формирования исторических данных
Как быстро сконструировать исторические признаки для модели и не допустить ошибку. Рассмотрим два способа.
На самом деле об одном из них я уже рассказывал ранее. Он заключается в объединении таблицы с ее же копией, сдвинутой на заданное количество периодов. Данная логика реализована в функции get_shift_data. Также написан ее более продвинутый аналог get_shift_data_tilln, возвращающий все признаки раньше заданного.
Другим способом является использование метода shift библиотеки Pandas. Рассмотрим игрушечный набор о заработках людей по месяцам (код для генерации представлен в конце статьи):
Рассмотренным ранее способом для получения матрицы с текущим и за три прошлых месяцев доходами можно было получить с помощью следующего кода:
n_m_b = 3 df_hist = general.hist_feat.get_shift_data_tilln(data[['dates', 'names', 'income']],'dates','income', n_m_b)
Отобразим результаты для человека с инициалами "V A":
va_ch = data[data['names']=='V A'].sort_values(by='dates') va1 = df_hist[df_hist['names']=='V A'].sort_values(by='dates')
DataFrame va_ch содержит первоначальные данные, из которых часть в результирующую таблицу не попала, так как не содержит сведения обо всех прошлых периодах:
Для устранения потери данных в df_hist следует дополнить исходную таблицу data пропущенных периодов, как указывалось в статье.
Для получения аналогичных результатов с применением shift следует отсортировать данные, так как функция не проверяет упорядоченность по времени данных, а просто сдвигает заданный столбец (на место отсутствующих полей либо ставит nan, либо заданное значение). При этом есть возможность сдвигать по группам, так как нас интересуют доходы разных людей:
data.sort_values(by='dates', inplace=True) for i in range(1,n_m_b+1): data[f'income_{str(i)}'] = data.groupby('names')['income'].shift(i) va2 = data[data['names']=='V A'].sort_values(by='dates') va2.dropna(inplace=True)
Данные разнятся по сравнению с прошлыми результатами, так как в первом случае проверялась непрерывность значений, а во втором - нет (например, для записи с датой 2011-04 в качестве прошлых месяцев фигурируют месяцы из 2010 года).
Поэтому дополнительно потребуется заполнить пропущенные значения времени (подробнее в статье):
pairs = general.hist_feat.full_period_data(data['dates'], data['names'].unique(), freq = 'M', fmt_str=None)\ .rename(columns={0:'dates',1:'names'}) data = pd.merge(pairs, data, on=['names', 'dates'], how='left')
Если теперь опять вызвать код из позапрошлого блока, получим:
Теперь две таблицы дают одинаковый результат. Ниже представлен код для генерации использованного в статье набора данных:
import pandas as pd import numpy as np import string # генерируем игрушечные данные np.random.seed(0) letters = np.array(list(string.ascii_uppercase)) data_size = 100 people_num = data_size//50 start_date = '2010-01' dates = pd.period_range(start_date, freq='M', periods=data_size) # людям соответствуют по две случайные буквы алфавита (имя, фамилия) names = np.random.choice(letters, size=(people_num,2)) # склеим буквы имен и фамилий names = pd.DataFrame(names).apply(lambda x:(x[0]+' '+x[1]), axis=1) # случайно выберем (с возвращением) людей для дополнения наших данных # до заданного размера names = names.sample(n=data_size, replace=True, axis=0) # доходы людей по дням от 1 до 7 условных единиц income = np.random.randint(1,8, size=data_size) data = pd.DataFrame({'dates':dates,'names':names,'income':income}) data = data.sample(frac=1)