Je ne pouvais pas sortir pendant la Golden Week, alors j'ai essayé "Predict Future Sales" de kaggle.
Ce thème prédit le nombre de produits vendus le mois prochain (novembre 2015) à partir des données agrégées quotidiennement (janvier 2013 à octobre 2015).
La caractéristique de ces données est
・ Quoi qu'il en soit, il y a beaucoup de données ・ Le type de magasin et le type d'article sont différents entre les données d'entraînement et les données de test. ・ Étant donné que les données d'entraînement sont quotidiennes et que le volume de ventes mensuel est prévu, il est nécessaire de faire correspondre le format avec les données de test. ・ Puisqu'il existe des éléments de séries chronologiques, il est nécessaire d'ajouter une quantité de fonctionnalités avec un décalage.
Faisons le.
Cette fois, je l'ai utilisé comme référence. kaggle: Feature engineering, xgboost
import copy
import numpy as np
import pandas as pd
from itertools import product
import matplotlib.pyplot as plt
import seaborn as sns
from lightgbm.sklearn import LGBMRegressor
from lightgbm import plot_importance
from sklearn.preprocessing import LabelEncoder
pd.set_option('display.max_rows', 100)
day_train_data = pd.read_csv('path/sales_train.csv')
month_test_data = pd.read_csv('path/test.csv')
item_categories = pd.read_csv('path/item_categories.csv')
items = pd.read_csv('path/items.csv')
shops = pd.read_csv('path/shops.csv')
Cette fois, 'date' dans day_train_data n'est pas utilisée, supprimez-la d'abord.
day_train_data.drop('date', axis=1, inplace=True)
Si vous cochez le 'nom_catégorie_élément' des catégories_élément, les catégories sont divisées avant et après le'- '.
item_categories['item_category_name'].head()
# [Résultat de sortie]
# 0 PC - Гарнитуры/Наушники
# 1 Аксессуары - PS2
# 2 Аксессуары - PS3
# 3 Аксессуары - PS4
# 4 Аксессуары - PSP
# Name: item_category_name, dtype: object
Nous allons décomposer ceci et étiqueter l'encoder. De plus, certains d'entre eux ont des noms similaires, je vais donc les corriger.
item_categories['split_name'] = item_categories['item_category_name'].str.split('-')
item_categories['big_category_name'] =\
item_categories['split_name'].map(lambda x: x[0].strip())
item_categories['sub_category_name'] =\
tem_categories['split_name'].map(lambda x: x[1].strip() if len(x) > 1 else x[0].strip())
item_categories.loc[item_categories['big_category_name'] == 'Чистые носители(штучные)', 'big_category_name'] = 'Чистые носители (шпиль)'
# label encoding
item_categories['big_category_id'] = LabelEncoder().fit_transform(item_categories['big_category_name'])
item_categories['sub_category_id'] = LabelEncoder().fit_transform(item_categories['sub_category_name'])
#Organiser les données
item_categories = item_categories[['item_category_id', 'big_category_id', 'sub_category_id']]
Comme pour les catégories, vous pouvez obtenir des informations sur la ville à partir de 'shop_name' dans les magasins, alors faisons-en une variable. Au fait, il y a des fautes de frappe, je vais donc les corriger également.
shops.loc[shops['shop_name'] == 'Сергиев Посад ТЦ "7Я"', 'shop_name'] = 'СергиевПосад ТЦ "7Я"'
shops['city_name'] = shops['shop_name'].map(lambda x: x.split(' ')[0])
shops.loc[shops['city_name'] == '!Якутск', 'city_name'] = 'Якутск'
# label encoding
shops['city_id'] = LabelEncoder().fit_transform(shops['city_name'])
#Organiser les données
shops = shops[['shop_id', 'city_id']]
Si vous regardez "shop_name", il a un nom similaire. En vérifiant les cahiers de kaggle, ça a la même apparence, donc je vais le réparer. Il y en a plusieurs autres.
shops[(shops['shop_id'] == 0) | (shops['shop_id'] == 57)]
[Résultat de sortie]
shop_id | shop_name | |
0 | !Якутск Орджоникидзе, 56 фран | 0 |
57 | Якутск Орджоникидзе, 56 | 57 |
Les fichiers à modifier sont day_train_data et month_test_data. Dans ce programme, je pense qu'il vaut mieux ne pas jouer avec les magasins. (Parce que je ferai la fusion plus tard, je tomberai dans l'enfer de la quantité de données.)
# shop_Modifier l'identifiant
day_train_data.loc[day_train_data['shop_id'] == 0, 'shop_id'] = 57
month_test_data.loc[month_test_data['shop_id'] == 0, 'shop_id'] = 57
day_train_data.loc[day_train_data['shop_id'] == 1, 'shop_id'] = 58
month_test_data.loc[month_test_data['shop_id'] == 1, 'shop_id'] = 58
day_train_data.loc[day_train_data['shop_id'] == 10, 'shop_id'] = 11
month_test_data.loc[month_test_data['shop_id'] == 10, 'shop_id'] = 11
Nous supprimerons les données dont les valeurs sont significativement différentes dans item_price et item_cnt_day. De plus, étant donné que item_price a une valeur négative, nous la corrigerons avec la valeur médiane des articles vendus au même endroit au même moment.
#Supprimer les valeurs aberrantes
day_train_data.drop(day_train_data[day_train_data['item_price'] >= 100000].index, inplace=True)
day_train_data.drop(day_train_data[day_train_data['item_cnt_day'] > 1000].index, inplace=True)
# item_Valeur d'erreur de prix fixe
median = day_train_data[(day_train_data['date_block_num'] == 4) & (day_train_data['shop_id'] == 32) & (day_train_data['item_id'] == 2973) & (day_train_data['item_price'] >= 0)]['item_price'].median()
day_train_data.loc[day_train_data['item_price'] < 0, 'item_price'] = median
Je souhaite créer un ensemble de données. L'ensemble de données créé cette fois ressemble à des données de panel incomplètes qui combinent day_train_data et month_test_data. De plus, étant donné que c'est l'article de chaque boutique dans month_test_data qui est prédit, je pense qu'il est également possible de copier month_test_data par la quantité de date_block_num et de créer des données de panneau complètes qui collent verticalement. Cependant, si vous faites cette méthode, une partie de day_train_data sera gaspillée, donc cette fois j'aimerais le faire avec la première.
# data_Créer un ensemble
data_set = []
cols = ['date_block_num', 'shop_id', 'item_id']
for date_num in range(34):
sales = day_train_data[day_train_data['date_block_num'] == date_num]
data_set.append(np.array(list(product([date_num],
sales['shop_id'].unique(),
sales['item_id'].unique()))))
data_set = pd.DataFrame(np.vstack(data_set), columns=cols)
data_set.sort_values(cols, inplace=True)
data_set = data_set.reset_index(drop=True)
# month_test_fusion de données
month_test_data['date_block_num'] = 34
month_test_data['date_block_num'] = month_test_data['date_block_num']
month_test_data['shop_id'] = month_test_data['shop_id']
month_test_data['item_id'] = month_test_data['item_id']
data_set = pd.concat([data_set, month_test_data], ignore_index=True, sort=False, keys=cols)
# data_set = data_set + shops [city_id]
data_set = pd.merge(data_set, shops, on=['shop_id'], how='left')
# data_set = data_set + items [item_category_id]
data_set = pd.merge(data_set, items[['item_id', 'item_category_id']], on=['item_id'], how='left')
# data_set = data_set + item_categories [big_category_id, sub_category_id]
data_set = pd.merge(data_set, item_categories, on=['item_category_id'], how='left')
Je souhaite créer la quantité de ventes mensuelles à partir de day_train_data. Cette variable devient la variable objective. En regardant kaggle, il semble que l'écrêtage de cette variable améliorera la précision de la prédiction.
# item_sales_Créer un jour
day_train_data['item_sales_day'] = day_train_data['item_price'] * day_train_data['item_cnt_day']
# item_cnt_Créer et fusionner le mois
merge_data = day_train_data.groupby(['date_block_num', 'shop_id', 'item_id'], as_index=False)['item_cnt_day'].agg('sum').rename(columns={'item_cnt_day': 'item_cnt_month'})
data_set = pd.merge(data_set, merge_data, on=cols, how='left')
data_set['item_cnt_month'] = data_set['item_cnt_month'].fillna(0).clip(0, 20)
Représentons graphiquement l'élément mensuel_cnt_month.
plot_data = day_train_data.groupby(['date_block_num'], as_index=False)['item_cnt_day'].agg('sum').rename(columns={'item_cnt_day': 'item_cnt_month'})
plt.figure(figsize=(20, 10))
sns.lineplot(data=plot_data, x='date_block_num', y='item_cnt_month')
Il diminue d'année en année, et le nombre d'unités vendues augmente de la même manière chaque décembre, confirmant la périodicité. Si vous mettez un tel endroit comme variable, la précision augmentera. Je voudrais examiner de plus près la décomposition variable.
import statsmodels.api as sm
res = sm.tsa.seasonal_decompose(plot_data['item_cnt_month'], freq=12)
plt.subplots_adjust(hspace=0.3)
plt.figure(figsize=(15, 9))
plt.subplot(411)
plt.plot(res.observed, lw=.6, c='darkblue')
plt.title('observed')
plt.subplot(412)
plt.plot(res.trend, lw=.6, c='indianred')
plt.title('trend')
plt.subplot(413)
plt.plot(res.seasonal, lw=.6, c='indianred')
plt.title('seasonal')
plt.subplot(414)
plt.plot(res.resid, lw=.6, c='indianred')
plt.title('residual')
Ici, nous diffuserons les données une fois.
data_set.fillna(0, inplace=True)
data_set['date_block_num'] = data_set['date_block_num'].astype(np.uint8)
data_set['shop_id'] = data_set['shop_id'].astype(np.uint8)
data_set['item_id'] = data_set['item_id'].astype(np.uint16)
data_set['item_cnt_month'] = data_set['item_cnt_month'].astype(np.float16)
data_set['ID'] = data_set['ID'].astype(np.uint32)
data_set['city_id'] = data_set['city_id'].astype(np.uint8)
data_set['item_category_id'] = data_set['item_category_id'].astype(np.uint8)
data_set['big_category_id'] = data_set['big_category_id'].astype(np.uint8)
data_set['sub_category_id'] = data_set['sub_category_id'].astype(np.uint8)
Si vous regardez cela, vous pouvez voir qu'il y a des tendances et une saisonnalité.
Feature Engineering Cette fonction sera beaucoup utilisée cette fois, mais attention car elle utilise temporairement beaucoup de mémoire.
def feature_lags(df, lags, lag_col):
tmp = df[['date_block_num', 'shop_id', 'item_id', lag_col]]
for lag in lags:
shifted = tmp.copy()
shifted.columns = ['date_block_num', 'shop_id', 'item_id', str(lag_col) + '_lag_' + str(lag)]
shifted['date_block_num'] += lag
shifted = shifted[shifted['date_block_num'] <= 34]
df = pd.merge(df, shifted, on=['date_block_num', 'shop_id', 'item_id'], how='left', copy=False)
return df
#target_lags
data_set = feature_lags(df=data_set, lags=[1, 2, 3, 6, 12], lag_col='item_cnt_month')
# date_avg_item_cnt
date_avg_item_cnt = data_set.groupby(['date_block_num'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_avg_item_cnt'})
data_set = pd.merge(data_set, date_avg_item_cnt, on=['date_block_num'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_avg_item_cnt')
data_set.drop(['date_avg_item_cnt'], axis=1, inplace=True)
# date_item_avg_item_cnt
date_item_avg_item_cnt = data_set.groupby(['date_block_num', 'item_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_item_avg_item_cnt'})
data_set = pd.merge(data_set, date_item_avg_item_cnt, on=['date_block_num', 'item_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_item_avg_item_cnt')
data_set.drop(['date_item_avg_item_cnt'], axis=1, inplace=True)
# date_shop_avg_item_cnt
date_shop_avg_item_cnt = data_set.groupby(['date_block_num', 'shop_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_shop_avg_item_cnt'})
data_set = pd.merge(data_set, date_shop_avg_item_cnt, on=['date_block_num', 'shop_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_shop_avg_item_cnt')
data_set.drop(['date_shop_avg_item_cnt'], axis=1, inplace=True)
# date_category_avg_item_cnt
date_category_avg_item_cnt = data_set.groupby(['date_block_num', 'item_category_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_category_avg_item_cnt'})
data_set = pd.merge(data_set, date_category_avg_item_cnt, on=['date_block_num', 'item_category_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_category_avg_item_cnt')
data_set.drop(['date_category_avg_item_cnt'], axis=1, inplace=True)
# date_shop_category_avg_item_cnt
date_shop_category_avg_item_cnt = data_set.groupby(['date_block_num', 'shop_id', 'item_category_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_shop_category_avg_item_cnt'})
data_set = pd.merge(data_set, date_shop_category_avg_item_cnt, on=['date_block_num', 'shop_id', 'item_category_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_shop_category_avg_item_cnt')
data_set.drop(['date_shop_category_avg_item_cnt'], axis=1, inplace=True)
# date_shop_bigcat_avg_item_cnt
date_shop_bigcat_avg_item_cnt = data_set.groupby(['date_block_num', 'shop_id', 'big_category_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_shop_bigcat_avg_item_cnt'})
data_set = pd.merge(data_set, date_shop_bigcat_avg_item_cnt, on=['date_block_num', 'shop_id', 'big_category_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_shop_bigcat_avg_item_cnt')
data_set.drop(['date_shop_bigcat_avg_item_cnt'], axis=1, inplace=True)
# date_shop_subcat_avg_item_cnt
date_shop_subcat_avg_item_cnt = data_set.groupby(['date_block_num', 'shop_id', 'sub_category_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_shop_subcat_avg_item_cnt'})
data_set = pd.merge(data_set, date_shop_subcat_avg_item_cnt, on=['date_block_num', 'shop_id', 'sub_category_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_shop_subcat_avg_item_cnt')
data_set.drop(['date_shop_subcat_avg_item_cnt'], axis=1, inplace=True)
# date_bigcat_avg_item_cnt
date_bigcat_avg_item_cnt = data_set.groupby(['date_block_num', 'big_category_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_bigcat_avg_item_cnt'})
data_set = pd.merge(data_set, date_bigcat_avg_item_cnt, on=['date_block_num', 'big_category_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_bigcat_avg_item_cnt')
data_set.drop(['date_bigcat_avg_item_cnt'], axis=1, inplace=True)
# date_subcat_avg_item_cnt
date_subcat_avg_item_cnt = data_set.groupby(['date_block_num', 'sub_category_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_subcat_avg_item_cnt'})
data_set = pd.merge(data_set, date_subcat_avg_item_cnt, on=['date_block_num', 'sub_category_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_subcat_avg_item_cnt')
data_set.drop(['date_subcat_avg_item_cnt'], axis=1, inplace=True)
# date_city_avg_item_cnt
date_city_avg_item_cnt = data_set.groupby(['date_block_num', 'city_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_city_avg_item_cnt'})
data_set = pd.merge(data_set, date_city_avg_item_cnt, on=['date_block_num', 'city_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_city_avg_item_cnt')
data_set.drop(['date_city_avg_item_cnt'], axis=1, inplace=True)
# date_item_city_avg_item_cnt
date_item_city_avg_item_cnt = data_set.groupby(['date_block_num', 'item_id', 'city_id'], as_index=False)['item_cnt_month'].agg('mean').rename(columns={'item_cnt_month': 'date_item_city_avg_item_cnt'})
data_set = pd.merge(data_set, date_item_city_avg_item_cnt, on=['date_block_num', 'item_id', 'city_id'], how='left')
data_set = feature_lags(df=data_set, lags=[1], lag_col='date_item_city_avg_item_cnt')
data_set.drop(['date_item_city_avg_item_cnt'], axis=1, inplace=True)
# item_avg_item_price
item_avg_item_price = day_train_data.groupby(['item_id'], as_index=False)['item_price'].agg('mean').rename(columns={'item_price': 'item_avg_item_price'})
data_set = pd.merge(data_set, item_avg_item_price, on=['item_id'], how='left')
# date_item_avg_item_price
date_item_avg_item_price = day_train_data.groupby(['date_block_num', 'item_id'], as_index=False)['item_price'].agg('mean').rename(columns={'item_price': 'date_item_avg_item_price'})
data_set = pd.merge(data_set, date_item_avg_item_price, on=['date_block_num', 'item_id'], how='left')
lags = [1, 2, 3, 4, 5, 6]
data_set = feature_lags(df=data_set, lags=lags, lag_col='date_item_avg_item_price')
for lag in lags:
data_set['delta_price_lag_' + str(lag)] = (data_set['date_item_avg_item_price_lag_' + str(lag)] - data_set['item_avg_item_price'] / data_set['item_avg_item_price'])
def select_trend(row):
for lag in lags:
if row['delta_price_lag_' + str(lag)]:
return row['delta_price_lag_' + str(lag)]
return 0
data_set['delta_price_lag'] = data_set.apply(select_trend, axis=1)
data_set['delta_price_lag'].fillna(0, inplace=True)
#Supprimer les données inutilisées
features_to_drop = ['item_avg_item_price', 'date_item_avg_item_price']
for lag in lags:
features_to_drop += ['date_item_avg_item_price_lag_' + str(lag)]
features_to_drop += ['delta_price_lag_' + str(lag)]
data_set.drop(features_to_drop, axis=1, inplace=True)
date_shop_sales = day_train_data.groupby(['date_block_num', 'shop_id'], as_index=False)['item_sales_day'].agg('sum').rename(columns={'item_sales_day': 'date_shop_sales'})
data_set = pd.merge(data_set, date_shop_sales, on=['date_block_num', 'shop_id'], how='left')
shop_avg_sales = day_train_data.groupby(['shop_id'], as_index=False)['item_sales_day'].agg('mean').rename(columns={'item_sales_day': 'shop_avg_sales'})
data_set = pd.merge(data_set, shop_avg_sales, on=['shop_id'], how='left')
data_set['delta_sales'] = (data_set['date_shop_sales'] - data_set['shop_avg_sales']) / data_set['shop_avg_sales']
data_set = feature_lags(df=data_set, lags=[1], lag_col='delta_sales')
data_set.drop(['date_shop_sales', 'shop_avg_sales', 'delta_sales'], axis=1, inplace=True)
data_set['month'] = data_set['date_block_num'] % 12
days = pd.Series([31,28,31,30,31,30,31,31,30,31,30,31])
data_set['day'] = data_set['month'].map(days)
data_set['day'] = data_set['day'].astype(np.uint8)
Mettez 0 dans NaN par fusion avant de séparer les données de data_set.
data_set.fillna(0, inplace=True)
Ceci termine le data_set. Je publierai les informations de data_set. Si vous ne lancez pas ceci, vous aurez plus du double de la quantité de données.
data_set.info()
""" out
<class 'pandas.core.frame.DataFrame'>
Int64Index: 11128004 entries, 0 to 11128003
Data columns (total 29 columns):
date_block_num uint8
shop_id uint8
item_id uint16
item_cnt_month float16
ID uint32
city_id uint8
item_category_id uint8
big_category_id uint8
sub_category_id uint8
item_cnt_month_lag_1 float16
item_cnt_month_lag_2 float16
item_cnt_month_lag_3 float16
item_cnt_month_lag_6 float16
item_cnt_month_lag_12 float16
date_avg_item_cnt_lag_1 float16
date_item_avg_item_cnt_lag_1 float16
date_shop_avg_item_cnt_lag_1 float16
date_category_avg_item_cnt_lag_1 float16
date_shop_category_avg_item_cnt_lag_1 float16
date_shop_bigcat_avg_item_cnt_lag_1 float16
date_shop_subcat_avg_item_cnt_lag_1 float16
date_bigcat_avg_item_cnt_lag_1 float16
date_subcat_avg_item_cnt_lag_1 float16
date_city_avg_item_cnt_lag_1 float16
date_item_city_avg_item_cnt_lag_1 float16
delta_price_lag float64
delta_sales_lag_1 float64
month uint8
day uint8
dtypes: float16(17), float64(2), uint16(1), uint32(1), uint8(8)
memory usage: 764.1 MB
"""
Ensuite, créez train_data et test_data.
target_col = 'item_cnt_month'
exclude_cols = ['ID', target_col]
feature_cols = []
for col in train_data.columns:
if col not in exclude_cols:
feature_cols.append(col)
x_train = train_data[train_data['date_block_num'] <= 32][feature_cols]
y_train = train_data[train_data['date_block_num'] <= 32][target_col]
x_val = train_data[train_data['date_block_num'] == 33][feature_cols]
y_val = train_data[train_data['date_block_num'] == 33][target_col]
x_test = test_data[feature_cols]
Tout d'abord, j'aimerais le faire par défaut sans réglage.
ts = time.time()
gbm = LGBMRegressor(n_jobs=-1)
gbm.fit(x_train, y_train,
eval_metric='rmse',
eval_set=(x_val, y_val))
time.time() - ts
# rmse: 0.948226
# time:23.153272
C'était 0.948226 sans réglage. Il a fallu moins de 30 secondes pour apprendre beaucoup plus rapidement que Random Forest.
Ensuite, je voudrais changer max_depth et n_estimators.
ts = time.time()
gbm = LGBMRegressor(n_estimators=10000, max_depth=10, n_jobs=-1)
gbm.fit(x_train, y_train,
early_stopping_rounds=20,
eval_metric='rmse',
eval_set=(x_val, y_val),
verbose=1)
time.time() - ts
# Early stopping, best iteration is: [105] valid_0's rmse: 0.943153
# time30.228255
Le meilleur score était lorsque n_estimators était de 105. Il n'y a pas beaucoup de changement dans rmse.
Visualisons feature_importances. date_item_avg_item_cnt_lag_1 fonctionne mieux.
J'ai soumis le résultat de la prédiction avec ce modèle à kaggle.
Le résultat était de 0,96079.
Cette fois, j'ai fait une prédiction en faisant des données en référence aux cahiers de kaggle.
Je n'ai pas encore réglé les hyper paramètres, donc La prochaine fois, j'aimerais m'accorder avec LightGBM lunar, une bibliothèque appelée optuna.