时间序列特征汇总
特征一、时间特征
特征二、类别mean encoding特征
特征三、统计特征
特征四:滞后历史特征
特征五:序列特征
特征六:高阶特征
特征七:外部特征
稍微总结一下,时间序列中的特征,主要针对机器学习树模型,因为是时序数据,所以和寻常的机器学习特征略有不同,比如关注时间特征,滞后特征,滑窗特征等。
特征一、时间特征
import datetime
import pandas as pd
df['ds']=df['ds'].astype(str)
df['ds'] = df['ds'].apply(lambda x: datetime.datetime.strptime(x, "%Y-%m-%d"))
df['year']=df['ds'].dt.year
df['quarter']=df['ds'].dt.quarter
df['month']=df['ds'].dt.month
df['dayofweek']=df['ds'].dt.dayofweek
df['week']=df['ds'].dt.week
诸如此类,更多相信内容请参照一下文档https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
还有格外需要手工生成的时间段切分:比如讲一天分为上午,下午,晚上这样的特征,可以使用apply,配合lambda匿名函数
df['half']=df['hour'].apply(lambda x: 1 if x<'12' else 0)
对于出现的异常值,年底出现促销,导致销售波动,对该天的数据做异常标记
df['outlier']=df['ds'].apply(lambda x: 1 if x=='2018-12-31' else 0)
还如,定义是否为春节月,是否为月末等,对这类具有明显销售波动的带有时间点打标签。这部分特征是十分基础且重要。
特征二、类别mean encoding特征
#针对类别变量可以有如下方法
#Mean Encoding
#比如门店预测
#带有城市标签,如果用OneHotEncoder转换为0-1特征就太多了
#我们可以使用mean encoding方式,构建均值特征
city_mean=df.groupby(['city']).agg({'y': ['mean']})
city_mean.reset_index(inplace=True)
city_mean.columns = ['city','city_mean']
#其他的信息,比如省份等亦是如此
df = pd.merge(df,city_mean,on='city',how='inner')
还有一种类似的count encoding,
其实应该归于统计特征
至于实现方式那就是
city_count=df.groupby(['city']).agg({'y': ['count']})
特征三、统计特征
如mean, median, max, min, std
需要注意的是windows的选择
如果是月的话,建议选择三 如果是日,且有周的规律性,则应该选择7,也可以同时存在多个颗粒度的滑窗特征。比如,选择了滑窗4,同时使用滑窗12,那就是季度。当然你也可是多尝试,所以调参侠的乐趣/苦逼,也就在这里了。滑窗也是一种对数据的平滑。一定不要忘记,把数据的顺序换过来。
df.sort_values(['store_code','ds'],ascending=[True,True],inplace=True)
f_min = lambda x: x.rolling(window=4, min_periods=1).min()
f_max = lambda x: x.rolling(window=4, min_periods=1).max()
f_mean = lambda x: x.rolling(window=4, min_periods=1).mean()
f_std = lambda x: x.rolling(window=4, min_periods=1).std()
f_median=lambda x: x.rolling(window=4, min_periods=1).median()
function_list = [f_min, f_max, f_mean, f_std,f_median]
function_name = ['min', 'max', 'mean', 'std','median']
for i in range(len(function_list)):
df[('stat_%s' % function_name[i])] = df.sort_values('ds').groupby(['store_code'])['y'].apply(function_list[i])
说明一点,这里为何没有使用sum聚合函数,因为有了mean,所以就没有必要使用sum了,因为sum是可以依据mean求得(可以依据一个特征直接得到的另一个变量的就属于冗余特征)
还有峰度,偏度等
有一个在时间序列方面非常出名的特征库:tsfresh
https://tsfresh.readthedocs.io/en/latest/api/tsfresh.feature_extraction.html
提供了非常多的特征扩展,我在之前的一篇文章中有提到,感兴趣的读者,可以前往一看。
tsfresh已经可以生成这么多的特征了,为何我们不直接调包,还要花这么大的力气来手工一个一个实现呢?
针对这两点疑问,有如下解释,虽然目前计算资源较为充裕,但我们也不希望在模型准确率接近的情况下,一个自动生成了300维度的特征花费两个小时,一个自己扩展了30维度的特征花费3分钟,不断迭代中的等待时间是非常昂贵的,所以我个人倾向于手工实现,做到心中有数,否则和黑匣子有什么区别。
其二,我们在很多情况下还是要追求模型的可解释性,少而精的特征,对于指导运营人员,具有非常大的价值,需要明白知道特征的含义。
特征四:滞后历史特征
for i in [1,2,4,8,12,24,52]:
df["lag_{}".format(i)] = df.groupby('store_code')['y'].shift(i)
这样的形式也是可以的,因为我这里是周的销售预测,所以我比较关注,上一周的,上两周的,四周以前的,也就是一个月前,以及8周,就是两月前。
这里是周销售预测,所以更关注上一周/上两周/四周前/8周的历史数据。
以上生成滞后特征,于是就可以方便的计算同/环比。
需要注意的是,只能是用滞后的数据,不能基于y值生成特征,否则就是数据穿越了
df['huanbi_1_2'] = df['last_1_sale'] / df['last_2_sale']
df['last_14_2_sale'] = (df['last_14_sale'] - df['last_2_sale']) / df['last_14_sale']
df['sale_uplift'] = (data['last_1_sale'] - data['last_2_sale']) / data['last_2_sale']
特征五:序列特征
不太好定义这类特征名称,姑且叫着序列特征
当面对众多序列的时候,也可以统计时间序列中单个序列的特性,比如,周期性,平稳性,复杂性(eg.排序熵)等等单个时间序列本身具有的信息。代码和具体讲解,见个人的另一篇博客
特征六:高阶特征
若有更多的信息,我们是可以利用多个特征进行融合,比如,我有门店开业时长,平均营业额,门店销售方差等等,可以利用这些信息聚类。
理由是:把类别标签,作为一个特征,相同的类别,理应具有相似的曲线,具有相似特性的数据,生成相同的数据特征。
当然有读者肯定有疑问了,聚类是无监督学习,事先无法知道聚类的个数,这里建议使用一点经验值,或者使用聚类的评估指标,如果轮廓系数,得到一个较为可靠的类别数。
def store_cluster(data):
data_.drop_duplicates(subset=['store_code'],inplace=True)
data_un=data_[['store_code','shop_mean','open_length','std_7']].set_index(['store_code'])
data_un.fillna(0, inplace=True)
columns_to_normalize= ['shop_mean','open_length','std_7']
data_un[columns_to_normalize] = data_un[columns_to_normalize].apply(lambda x: (x - x.mean()) / np.std(x))
kmeans= KMeans(n_clusters=4).fit(data_un)
data_un['cluster_id']=kmeans.predict(data_un)
data_un=data_un.reset_index()
return data_un[['store_code','cluster_id']]
刚提到了曲线和趋势,那么,我们也是可以依据多条序列的波动,依据波动,找到相似的波动曲线,作为同一类标签,这就是基于时间序列的一个聚类方式。
比如 DTW方法
如果说KMeans聚类是一种静态的使用欧式距离计算的聚类方法,DTW方法就是一种对多个序列具有延展或者压缩特性的度量距离的方法,考虑序列形状的相似,常用的算法如k-shape。关于该方法和代码见个人更新的另一篇文章。链接
除此之外还有傅里叶变换模块和函数如下:
from pykalman import KalmanFilter
当然对于多个类别数据,也可以使用embedding的方式
https://www.tensorflow.org/tutorials/text/word_embeddings
或者*TF-IDF,总的来说我这里列举的高阶特征,是利用多个特征进行融合,或者使用多条数据,求得相似性,减少信息冗余。当然也限于篇幅的原因,这部分非常重要的内容没有展开来讲,不过可以依据以上提到的关键词和资料很容易找到相关材料。
特征七:外部特征
很多数据科学竞赛本身不提供外部数据,但鼓励选手通过技术手段获取并利用外部数据,比如天气,比如节假日,对气温这样的特征,做分桶处理见pd.cut函数。
以上就是个人日常使用较多的特征,只是针对树模型特征生成过程进行简单的总结阐述,具体在建模过程中还要依据数据本身灵活多变,还有诸如,prophet模型的特征,数据预处理等等内容再次不做介绍,如有机会单独开篇。
————————————————