Kaggle M5 Competition Part 1 -EDA

Contents of table:



Kaggle M5 Competition Part 1 -EDA


미국 Wal-Mart 에서 주최한 매출예측 대회이다.

#ref:
https://mofc.unic.ac.cy/m5-competition/
https://www.kaggle.com/c/m5-forecasting-accuracy

#아래 코드는 Kaggle Grandmaster Rob Mulla 의 모델링을 기반으로 재구성하였습니다.

대회설명:
M5는 월마트에서 제공하는 계층적 판매 데이터를 사용하여, 향후 28 일 동안의 일일 판매를 예측하고 분포를 추정하는 것이 목표이다.
데이터에는 가격, 프로모션, 요일 및 특별 이벤트와 같은 설명 변수가 포함된다.

데이터셋:
calendar.csv - 제품 판매 날짜에 대한 정보를 포함.
sales_train_validation.csv - 제품 및 매장 별 일일 판매량 기록 데이터 포함 [d_1-d_1913]
sample_submission.csv - 제출 양식.
sell_prices.csv - 상점 및 날짜별로 판매 된 제품의 가격에 대한 정보를 포함.
sales_train_evaluation.cs - 제품 판매 포함 [d_1-d_1941]

1
2
3
4
5
6
7
8
9
10
11
12
13
import os
import pandas as pd
import numpy as np
import plotly_express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import matplotlib.pyplot as plt
import seaborn as sns
import gc
import warnings
warnings.filterwarnings('ignore')
from lightgbm import LGBMRegressor
import joblib

1. Fetch the data

1
2
3
4
5
6
sales = pd.read_csv('C:\\Eric\\Projects\\Kaggle_M5\Dataset\\sales_train_evaluation.csv')
sales.name = 'sales'
calendar = pd.read_csv('C:\\Eric\\Projects\\Kaggle_M5\Dataset\\calendar.csv')
calendar.name = 'calendar'
prices = pd.read_csv('C:\\Eric\\Projects\\Kaggle_M5\Dataset\\sell_prices.csv')
prices.name = 'prices'
1
sales.columns
Index(['id', 'item_id', 'dept_id', 'cat_id', 'store_id', 'state_id', 'd_1',
       'd_2', 'd_3', 'd_4',
       ...
       'd_1932', 'd_1933', 'd_1934', 'd_1935', 'd_1936', 'd_1937', 'd_1938',
       'd_1939', 'd_1940', 'd_1941'],
      dtype='object', length=1947)
1
2
3
4
5
#빈 칸 처리되어있는 d 1942 ~ 1969 col들에 0 입력
for d in range(1942,1970):
col = 'd_' + str(d)
sales[col] = 0
sales[col] = sales[col].astype(np.int16)

2. Downcasting

1
2
3
4
#기본 데이터셋의 용량이 큰 만큼, 메모리 다운이 필요. 
sales_bd = np.round(sales.memory_usage().sum()/(1024*1024),1)
calendar_bd = np.round(calendar.memory_usage().sum()/(1024*1024),1)
prices_bd = np.round(prices.memory_usage().sum()/(1024*1024),1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#캐글의 memory downcasting 코드를 참고하여 아래와 같이 메모리 다운. 
def downcast(df):
cols = df.dtypes.index.tolist()
types = df.dtypes.values.tolist()
for i,t in enumerate(types):
if 'int' in str(t):
if df[cols[i]].min() > np.iinfo(np.int8).min and df[cols[i]].max() < np.iinfo(np.int8).max:
df[cols[i]] = df[cols[i]].astype(np.int8)
elif df[cols[i]].min() > np.iinfo(np.int16).min and df[cols[i]].max() < np.iinfo(np.int16).max:
df[cols[i]] = df[cols[i]].astype(np.int16)
elif df[cols[i]].min() > np.iinfo(np.int32).min and df[cols[i]].max() < np.iinfo(np.int32).max:
df[cols[i]] = df[cols[i]].astype(np.int32)
else:
df[cols[i]] = df[cols[i]].astype(np.int64)
elif 'float' in str(t):
if df[cols[i]].min() > np.finfo(np.float16).min and df[cols[i]].max() < np.finfo(np.float16).max:
df[cols[i]] = df[cols[i]].astype(np.float16)
elif df[cols[i]].min() > np.finfo(np.float32).min and df[cols[i]].max() < np.finfo(np.float32).max:
df[cols[i]] = df[cols[i]].astype(np.float32)
else:
df[cols[i]] = df[cols[i]].astype(np.float64)
elif t == np.object:
if cols[i] == 'date':
df[cols[i]] = pd.to_datetime(df[cols[i]], format='%Y-%m-%d')
else:
df[cols[i]] = df[cols[i]].astype('category')
return df
1
2
3
sales = downcast(sales)
prices = downcast(prices)
calendar = downcast(calendar)
1
2
3
4
#메모리 다운 후의 메모리 사용량 체크. 
sales_ad = np.round(sales.memory_usage().sum()/(1024*1024),1)
calendar_ad = np.round(calendar.memory_usage().sum()/(1024*1024),1)
prices_ad = np.round(prices.memory_usage().sum()/(1024*1024),1)
1
2
3
4
5
6
7
8
9
10
11
12
#다운 캐스팅이 DataFrame의 메모리 사용량에 얼마나 많은 영향을 미쳤는지 시각화.1/4 미만으로 줄일 수 있음. 
dic = {'DataFrame':['sales','calendar','prices'],
'Before downcasting':[sales_bd,calendar_bd,prices_bd],
'After downcasting':[sales_ad,calendar_ad,prices_ad]}

memory = pd.DataFrame(dic)
memory = pd.melt(memory, id_vars='DataFrame', var_name='Status', value_name='Memory (MB)')
memory.sort_values('Memory (MB)',inplace=True)
fig = px.bar(memory, x='DataFrame', y='Memory (MB)', color='Status', barmode='group', text='Memory (MB)')
fig.update_traces(texttemplate='%{text} MB', textposition='outside')
fig.update_layout(template='seaborn', title='Effect of Downcasting')
fig.show()

3. Exploratory Data Analysis

1
2
3
4
5
6
walmart 에서 제공하는 세일즈 데이터는, wrt, 즉 with respect to [ cols ]
State: CA, WI, TX (3)
Store: CA_1, CA_2, TX_1, WI_1, ... (10)
Category: FOOD, HOBBIES, HOUSEHOLD (3)
Department:FOOD_1,2,3 , HOBBIES_1,2, ... (7)
item_id:: each unique id # (3,049)
1
2
3
4
5
6
7
8
9
10
11
#plotly_express 에서 제공하는 treemap 을 활용해서, 각 제품 id 를 count var로 잡고, data col 들의 관계를 directory 형태로 시각화.

group = sales.groupby(['state_id','store_id','cat_id','dept_id'],as_index=False)['item_id'].count().dropna()
group['USA'] = 'United States of America'
group.rename(columns={'state_id':'State','store_id':'Store','cat_id':'Category','dept_id':'Department','item_id':'Count'},inplace=True)
fig = px.treemap(group, path=['USA', 'State', 'Store', 'Category', 'Department'], values='Count',
color='Count',
color_continuous_scale= px.colors.sequential.Sunset,
title='Walmart: Distribution of items')
fig.update_layout(template='seaborn')
fig.show()

4. Melting the data

#4.1 Convert from wide to long format

1
머신러닝 포맷에 적합시키기 위해서는 와이드 형식의 판매 데이터 프레임을 긴 형식으로 변환이 필요하다. sales 데이터셋의 row 는 30490(== # of items), 데이터셋을 melt하게되면은 sales, calendar 30490 x 1969 = 60034810 개의 row 를 가지게 된다. 
1
df = pd.melt(sales, id_vars=['id', 'item_id', 'dept_id', 'cat_id', 'store_id', 'state_id'], var_name='d', value_name='sold').dropna()
1
2
df = pd.merge(df, calendar, on='d', how='left')
df = pd.merge(df, prices, on=['store_id','item_id','wm_yr_wk'], how='left')
1
2
3
4
5
6
7
#Store 별로 매출액합계를 violin plot 을 활용해서 시각화. 
group = df.groupby(['year','date','state_id','store_id'], as_index=False)['sold'].sum().dropna()
fig = px.violin(group, x='store_id', color='state_id', y='sold',box=True)
fig.update_xaxes(title_text='Store')
fig.update_yaxes(title_text='Total items sold')
fig.update_layout(template='seaborn',title='Distribution of Items sold wrt Stores',legend_title_text='State')
fig.show()

Author

Eric Park

Posted on

2020-09-22

Updated on

2020-09-22

Licensed under