【BI学习心得10-时间序列实战】

本文详细介绍了时间序列特征工程的多种方法,包括时间序列特征、类别meanencoding、统计特征、滞后历史特征、序列特征、高阶特征和外部特征的构建。通过实例展示了如何使用Python对数据进行预处理,如调整数据类型以减少内存占用,并介绍了Facebook的Prophet库进行时间序列预测。此外,还探讨了数据探索的重要步骤,如数据类型检查、缺失值处理和可视化。
摘要由CSDN通过智能技术生成


写在前面的话

稍微总结一下,时间序列中的特征,主要针对机器学习树模型,因为是时序数据,所以和寻常的机器学习特征略有不同,比如关注时间特征,滞后特征,滑窗特征等。

1.与时间特征有关的那些事

1.1时间序列特征


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)


还如,定义是否为春节月,是否为月末等,对这类具有明显销售波动的带有时间点打标签。这部分特征是十分基础且重要。

1.2类别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']})


1.3统计特征

如mean, median, max, min, std

如果是月的话,建议选择三 如果是日,且有周的规律性,则应该选择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。提供了非常多的特征扩展,我在之前的一篇文章中有提到,感兴趣的读者,可以前往一看。
tsfresh已经可以生成这么多的特征了,为何我们不直接调包,还要花这么大的力气来手工一个一个实现呢?

针对这两点疑问,有如下解释,虽然目前计算资源较为充裕,但我们也不希望在模型准确率接近的情况下,一个自动生成了300维度的特征花费两个小时,一个自己扩展了30维度的特征花费3分钟,不断迭代中的等待时间是非常昂贵的,所以我个人倾向于手工实现,做到心中有数,否则和黑匣子有什么区别。

其二,我们在很多情况下还是要追求模型的可解释性,少而精的特征,对于指导运营人员,具有非常大的价值,需要明白知道特征的含义。

1.4滞后历史特征


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']


1.5序列特征

不太好定义这类特征名称,姑且叫着序列特征。

当面对众多序列的时候,也可以统计时间序列中单个序列的特性,比如,周期性,平稳性,复杂性(eg.排序熵)等等单个时间序列本身具有的信息。

1.6高阶特征

若有更多的信息,我们是可以利用多个特征进行融合,比如,我有门店开业时长,平均营业额,门店销售方差等等,可以利用这些信息聚类。

理由是:把类别标签,作为一个特征,相同的类别,理应具有相似的曲线,具有相似特性的数据,生成相同的数据特征。

当然有读者肯定有疑问了,聚类是无监督学习,事先无法知道聚类的个数,这里建议使用一点经验值,或者使用聚类的评估指标,如果轮廓系数,得到一个较为可靠的类别数。


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的方式。或者*TF-IDF,总的来说我这里列举的高阶特征,是利用多个特征进行融合,或者使用多条数据,求得相似性,减少信息冗余。当然也限于篇幅的原因,这部分非常重要的内容没有展开来讲,不过可以依据以上提到的关键词和资料很容易找到相关材料。

1.7外部特征

很多数据科学竞赛本身不提供外部数据,但鼓励选手通过技术手段获取并利用外部数据,比如天气,比如节假日,对气温这样的特征,做分桶处理见pd.cut函数。

2.好用的小技巧

2.1python调整DataFrame的数据类型,减少内存占用

import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')
 
def reduce_mem_usage(df):
    """ iterate through all the columns of a dataframe and modify the data type
        to reduce memory usage.        
    """
    start_mem = df.memory_usage().sum() 
    print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))
    
    for col in df.columns:
        col_type = df[col].dtype
        
        if col_type != object:
            c_min = df[col].min()
            c_max = df[col].max()
            if str(col_type)[:3] == 'int':
                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)  
            else:
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)
        else:
            df[col] = df[col].astype('category')
 
    end_mem = df.memory_usage().sum() 
    print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))
    print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))
    return df
 
 
sample_feature = reduce_mem_usage(pd.read_csv('data_for_tree.csv'))

2.2Prophet: 时间序列预测库

prophet是facebook开源的python预测库,该库的api设计与sklearn很像,也是分为fit方法和predict方法。

prophet库可以帮助我们进行

  • Saturating Forecasts
  • Trend Changepoints
  • Seasonality, Holidays Effects
  • Multiplicative Seasonality
  • Uncertainty Intervals
  • Outliers
  • Non-Daily Data
  • Diagnostics

传入prophet的数据分为两列 dsy, 其中

  • ds是pandas的日期格式,样式类似与 YYYY-MM-DDfora dateorYYYY-MM-DD HH:MM:SS;
  • y列必须是数值型,代表着我们希望预测的值。

本文使用的是wiki网站日访问量(数值经过log处理)的csv数据文件。

2.3.1安装
pip3 install fbprophet
2.3.2导入数据
import pandas as pd
from fbprophet import Prophet

df = pd.read_csv('user_balance_table.csv',parse_dates=(['report_date']))
df.head()

在这里插入图片描述

2.3.3检查下df的数据类型
df.dtypes

在这里插入图片描述

ds列必须是pandas的datetime数据类型,我们使用pandas自带的pd.to_datetime将日期转为datetime类型。
在这里插入图片描述

2.3.4拟合数据

学习数据中的规律

prophet = Prophet()
prophet.fit(df)
2.3.5预测

生成一个未来的日期的dataframe,然后用训练好的模型prophet来predict。

生成未来日期的dateframe用到 Prophet.makefuturedataframe(periods)

future = prophet.make_future_dataframe(periods=365)
future.tail()

在这里插入图片描述

有了未来的日期,我们就可以使用学习到的趋势来预测未来日期的走势。

预测的结果包括如下变量

'ds', 'trend', 'yhat_lower', 'yhat_upper', 'trend_lower', 'trend_upper',
 'additive_terms', 'additive_terms_lower', 'additive_terms_upper',
 'weekly', 'weekly_lower', 'weekly_upper', 'yearly', 'yearly_lower',
 'yearly_upper', 'multiplicative_terms', 'multiplicative_terms_lower',
 'multiplicative_terms_upper', 'yhat'

我们只用 'ds','yhat','yhat_lower','yhat_upper'

forecast = prophet.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head()

在这里插入图片描述
Prophet.plot方法可以帮助我们可视化

fig1 = prophet.plot(forecast)
print(fig1)

在这里插入图片描述
成分分析

趋势是由不同的成分组成,比如总趋势、年、季节、月、周等等,我们要将这些成分从趋势中抽取出来看看不同成分的趋势情况

fig2 = prophet.plot_components(forecast)
print(fig2)

在这里插入图片描述
prophet库的API相对简单,并且由于使用了pandas标准的dataframe和matplotlib来显示数据,因此很适合python数据科学工作流程。

3.数据探索步骤

  1. 查看数据内容 df.info() df.head()
  2. 检查数据有没有缺失项,存在需要给定填充值,常用的填充值有-1,1
  3. 选择部分数据进行可视化,查找数据之间的关系

参考资料

1.时间序列树模型特征工程汇总
2.statsmodels官方文档
3.tsfresh

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水花

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值