之前做过一次Kaggle的时间序列竞赛数据集练习:优快云链接效果并不理想,之后在Kaggle的评论中又找到了各式各样的模型方法,其中我还手动还原过第三名的Entity Embedding:优快云链接。这个参赛方法中,使用了除了比赛给出的数据以外的外部数据(天气数据等)。而这次,我准备还原一个没有使用外部数据且方法较为简单,但是效果较好的策略。也就是第66名的策略。
详细的策略可以看这里 R语言版的源代码在这里 Kaggle页面可以看这里
我复现出来的NoteBook可以在Kaggle上看到。
特征工程 Feature Engineering
原策略中的特征工程制造了以下几个特征:
1、傅里叶级数展开的非常数项(包括正弦、余弦函数各自的前5项),可以在优快云链接与R语言源码中查看原理。
2、“日期趋势”项以及它的对数值(DateTrend和DateTrendLog)
3、对于假期以及促销事件,加入这些事件发生前后的衰减项
4、对于这些特殊事件,将这些事件发生的前后n天做0-1变量(stair-step变量)
5、加入了“是否明天闭店”、“是否昨日闭店”、“上周日是否闭店”等0-1变量
6、将日期分解为年月日等维度,并加入对应的二分类变量特征。
import warnings
warnings.filterwarnings("ignore")
import pandas as pd
from datetime import datetime
import numpy as np
df_train = pd.read_csv("./input/train.csv")
df_test = pd.read_csv("./input/test.csv")
df_store = pd.read_csv("./input/store.csv")
train_range = pd.date_range(df_train["Date"].min(),df_train["Date"].max(), freq='D')
test_range = pd.date_range(df_test["Date"].min(),df_test["Date"].max(), freq='D')
full_range = pd.date_range(df_train["Date"].min(),df_test["Date"].max(), freq='D')
# 根据full_range构建df_full
df_full = pd.concat([df_train,df_test],axis=0).drop(columns=["Id"])
weekday_dict = df_full[["Date","DayOfWeek"]].drop_duplicates()
# 构建全零的表格
df_all_zero = pd.DataFrame(full_range,columns=["Date"])
for col in ["Sales","Customers","Open","Promo","StateHoliday","SchoolHoliday"]:
df_all_zero[col]=0
df_all_zero = df_all_zero.merge(weekday_dict,on="Date",how="left")
df_all_stores = pd.DataFrame(df_full["Store"].unique(),columns=["Store"])
df_all_zero = df_all_zero.merge(df_all_stores,how="cross")
# 将全零表格填充到full表
df_all_zero = df_all_zero.set_index(["Store","Date"])
df_full = df_full.set_index(["Store","Date"])
df_full = df_full.combine_first(df_all_zero)
df_full = df_full.reset_index()
del df_all_stores,df_all_zero
# df_full = df_full.merge(df_store,on="Store")
# df_full = df_full.sort_values(["Store","Date"]).reset_index(drop=True)
def logger(func):
"""
装饰器,用于测量函数运行时间并打印函数名
"""
def log(*args, **kwargs):
# 获取当前时间作为开始时间
start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"Function {
func.__name__} started at {
start_time}")
# 调用原始函数
result = func(*args, **kwargs)
# 获取当前时间作为结束时间
end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"Function {
func.__name__} ended at {
end_time}")
return result
return log
class DataFrameClass():
@logger
def __init__(self,df:pd.DataFrame,df_store:pd.DataFrame,full_range,train_range,test_range):
self.data = df
self.full_range = full_range
self.train_range = train_range
self.test_range = test_range
self.store = df_store
self.store["Competition_OpenDate"] = pd.Series(map(self.build_Date,self.store["CompetitionOpenSinceYear"],self.store["CompetitionOpenSinceMonth"]))
# 如果一家店Sales为0,则也视为Close
idx = self.data[(self.data["Sales"]==0) & (self.data["Date"]<=self.train_range[-1])].index
self.data.loc[idx,"Open"] = 0
self.data["Close"] = 1-self.data["Open"]
date_trend_dict = {
i:j for i,j in zip(full_range,np.linspace(1,0,len(full_range),endpoint=False).tolist()[::-1])}
self.data["DateTrend"] = self.data["Date"].map(lambda x:date_trend_dict[x])
self.data["CloseOnSunday"] = 0
self.data.loc[self.data[(self.data["DayOfWeek"] == 7) & (self.data["Close"]==1)].index,"CloseOnSunday"]=1
def pre_process_data(self):
"""
数据预处理
"""
self.add_fourier_series()
self.get_dummy_col("DayOfWeek",drop_col="DayOfWeek_7",is_store=False)
self.get_dummy_col("StateHoliday",drop_col="StateHoliday_0",is_store=False)
unique_values = sorted(list(set([str(i) for i in self.data["StateHoliday"].unique()])))
value_dict = {
j:i+1 for i,j in enumerate(unique_values)}
self.get_continous_value("StateHoliday",value_dict,is_store=False)
for col in ["StoreType","Assortment","PromoInterval"]:
if col == "PromoInterval":
value_dict={
'Feb,May,Aug,Nov': 2, 'Jan,Apr,Jul,Oct': 3, 'Mar,Jun,Sept,Dec': 4, 'nan': 1}
else:
unique_values = sorted(list(set([str(i) for i in self.store[col].unique()])))
value_dict = {
j:i+1 for i,j in enumerate(unique_values)}
self.get_continous_value(col,value_dict,is_store=True)
self.data = self.data.merge(self.store,on="Store")
self.get_CompetitionOpen()
self.get_isPromo2()
self.get_Date_decompose()
self.get_dummy_col("month")
self.get_dummy_col("day")
for col in ["Promo","StateHoliday_b","StateHoliday_a","isPromo2"]:
self.get_is_Event_first_day(col)
self.get_Event_Started_Last_Date(col)
self.get_is_Event_first_day("StateHoliday_c")
idxs = self.data[(self.data["month"]==12) & (self.data["day"]==26)].index#12月26号需要特别处理为当天
self.data.loc[idxs,"StateHoliday_c_first_day"] = 1
self.get_Event_Started_Last_Date("StateHoliday_c")
self.get_is_Event_first_day("Open")
self.get_is_Event_first_day("Close")#Close_first_day相当于Closed,Open_first_day相当于Opened
# 是否第二天歇业
idxs = self.data[(self.data.shift(-1)["Close_first_day"]==1) & (self.data["Date"]!=full_range[-1])].index
self.data["TomorrowClosed"] = 0
self.data.loc[idxs,"TomorrowClosed"]=1
self.get_Event_Started_Last_Date("Close")
self.get_Last_Event_distance("Close")
self.get_Event_Started_Last_Date("Open")
self.get_Last_Event_distance("Open")
self.get_is_Event_first_day("CloseOnSunday")
self.get_Event_Started_Last_Date("CloseOnSunday")
self.data["WasClosedOnSunday"] = ((self.data["Date"]-self.data["CloseOnSunday_last_started"]).dt.days<7).astype(int)
self.get_Event_Start_NextDay("StateHoliday_c")
self.get_Event_Start_NextDay("Open")
self.get_Event_Start_NextDay("Close")
self.get_Next_Event_distance("Open") #Will be Closed for days
# LongClosedNextDate
self.data["LongClosed_first_day"]=0
idxs = self.data[(self.data["Close_first_day"]==1) & (self.data["Open_next_distance"]>5) & (self.data["Open_next_distance"]<180)].index
self.data.loc[idxs,"LongClosed_first_day"]=1
self.get_Event_Start_NextDay("LongClosed")
self.get_Next_Event_distance("LongClosed")
self.get_Event_Start_NextDay("StateHoliday_b")
self.get_Event_Start_NextDay("StateHoliday_a")
# LongOpenLastDate
self.data["LongOpen_first_day"]=0
idxs = self.data[(self.data["Open_first_day"]==1) & (self.data["Close_last_distance"]>5) & (self.data["Close_last_distance"]<180)]

最低0.47元/天 解锁文章
4056

被折叠的 条评论
为什么被折叠?



