Предобработка исторических данных
Покажем, как формировать исторические признаки для моделей машинного обучения. Описанные в статье шаблоны нацелены на многократное использование в реальных задачах.
Предположим, что мы располагаем информацией о доходах людей по дням, имеющей следующий вид (код для генерации представлен в конце статьи):
Нашей задачей является подготовка данных для предсказания доходов в последующем месяце на основании исторических сведений предыдущих месяцев. При этом предсказание на новый период производится 15 числа текущего.
Одним из этапов на пути к построению модели является формирование истории помесячных доходов за предыдущие периоды в качестве параметров.
Для этого потребуется научиться:
- преобразовывать строковые данные к типу даты и времени;
- сместить даты на 14 дней назад;
- сгруппировать данные помесячно.
Сначала загрузим данные и преобразуем числовой столбец дохода (income):
# считываем с параметром dtype='str' # чтобы избежать ошибок преобразований, о которых писал ранее df = pd.read_excel('time/data.xlsx', dtype='str') df['income'] = df['income'].astype('int')
Для преобразования столбца dates к типу даты и времени воспользуемся функцией Pandas to_datetime:
df['dates'] = pd.to_datetime(df['dates']) #format='%d-%m-%Y'
В качестве второго параметра можно задать формат даты и времени, если по умолчанию он распознается с ошибками.
Для смещения даты можно использовать объект Pandas Timedelta:
df['date_sh'] = df['dates'].map(lambda x: x - pd.Timedelta(days=14)) df['date_sh'] = df['date_sh'].map(lambda x: x.to_period('M'))
После смещения преобразовываем дни к типу Period с месячным интервалом, по которому будет удобно группировать (обратите внимание на столбец 'date_sh').
Затем группируем строки по столбцам 'date_sh' и 'names' с подсчетом суммы доходов в столбце 'income'.
df_gr = df.groupby(['date_sh', 'names'], as_index=False)['income'].sum()
Можно выборочно проверить, насколько правильно прошла группировка:
df[df['names']=='D D'] df_gr[df_gr['names']=='D D']
Также может понадобиться заполнить пропуски месяцев для отдельных людей нулями. Для этого сначала получим датафрейм, содержащий декартово произведение всех людей и рассматриваемых месяцев:
names_pers = pd.MultiIndex.from_product([df_gr['names'].unique(),df_gr['date_sh'].unique()]).to_frame().reset_index(drop=True) names_pers.columns = ['names', 'pers'] names_pers['pers'] = names_pers['pers'].map(lambda x: pd.Period(x, freq='M'))
Теперь объединим таблицы и заполним недостающие значения дохода нулями:
df_nullplus = pd.merge(df_gr, names_pers, left_on=['names','date_sh'], right_on=['names','pers'], how='outer').drop('date_sh', axis=1) df_nullplus = df_nullplus.rename(columns={'pers':'date_sh'}) df_nullplus['income'].fillna(0,inplace=True)
Как можно заметить, добавились отсутствующие в первоначальной таблице строки с нулевыми значениями.
Таким образом, мы сформировали базовую структуру исторических признаков, с которой удобно формировать признаки (например, выделить доход в предыдущем месяце или два месяца назад).
Код для генерации рассмотренных данных:
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//10 start_d = '2021-01-01' dates = pd.date_range(start_d, freq='D', 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) pd.DataFrame({'dates':dates,'names':names,'income':income}).to_excel('time/data.xlsx', index=False)