Kaggle竞赛:Rossmann Store Sales第66名策略复现

之前做过一次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)]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值