特征衍生——时序特征

时序特征的衍生方法

  • 通用特征衍生代码
# dataFrame
pd.to_datetime(df['time']) # 年月日时分秒 -> datetime64[ns]格式
'2022-01-03;02:31:52' -> '2022-01-03 02:31:52'
# numpy
df['time'].values.astype('datetime64[ns/D/..]')
df['time-D'] = df['time'].values.astype('datetime64[s/h/D/...]') # 降低时间精度
# 1. 通用时序信息提取,datetime64 -> dt.func
df['year'] = df['time'].dt.year / month / day / hour / minute / second
# 2. 其它自然周期信息提取,比如: 日期所在的季度 / 全年的第几周 / 一周的第几天 / 日期是否在周末 / 时间发生的时间是在上午、下午还是晚上
df['quarter'] = df['time'].quarter
df['weekofyear'] = df['time'].weekofyear
df['dayofweek'] = df['time'].dayofweek + 1
df['weekend'] = (df['dayofweek'] > 5).astype(int) 
df['hourPeriodOfday'] = (t['hour'] // 6).astype(int) + 1 # 当前小时所属一天的周期,凌晨、上午、下午、晚上,周期以6小时为划分依据
  • 为什么要提取这些时序信息?时序字段衍生的本质:增加分组的细粒度化
      分析:首先,时序特征的衍生其本质上就是对用户进行了更多不同维度的分组,该过程可以通过下图进行解释,在tenure时序特征进行特征衍生后,ID1-6号用户在所属年份列中就被划分到了2019年组中,即他们同为2019年入网的用户,而根据入网的季节进行划分,则ID为0、4、5的三个用户会被划归到第一季度入网用户组中,并且时序特征衍生的字段越多、对用户分组的维度也就越多:
    特征衍生——时序特征_第1张图片
      而对用户进行分组之所以能够帮助模型进行建模与训练,其根本原因也是因为有的时候,同一组内(或者是多组交叉)的用户会表现出相类似的特性(或者规律),从而能够让模型更快速的对标签进行更准确的预测,例如假设数据集如下所示,在原始数据集看来,标签取值毫无规律可言,但当我们对其进行时序特征的特征衍生后,立刻能发掘很多规律,例如第四季度用户都流失了、2019年第一季度用户流失都流失了等等,同样,这些通过观察就能看到的规律,也很快会被模型捕捉到,而这也是时序字段衍生能够帮助模型进行更好更快的预测的直观体现。
    特征衍生——时序特征_第2张图片
      当然,很多时候也是因为我们不知道什么样的分组能够有效的帮助到模型进行建模,因此往往需要衍生尽可能多的字段,来进行尽可能多的分组,而这些时序字段的衍生字段,也会在后续的建模过程中接受特征筛选的检验
  • 时序字段衍生的核心思路:自然周期和业务周期
      如何才能尽可能地衍生更多的时序字段呢?在进行了通用时间特征的衍生之后,接下来的时序特征衍生就需要同时结合自然周期和业务周期两个方面进行考虑。所谓自然周期,指的是对于时间大家普遍遵照或者约定俗成的一些规定,例如工作日周末、一周七天、一年四个季度等,这也就是此前我们进行的一系列特征衍生工作,此外其实还可以根据一些业务周期来进行时序特征的划分,例如对于部分旅游景点来说,暑假是旅游旺季,并且很多是以家庭为单位进行出游(学生暑假),因此可以考虑单独将8、9月进行标记,期间记录的用户会有许多共性,而组内用户的共性就将成为后续建模效果的保障;再比如6月、11月是打折季,也可以类似的单独设一列对6月、11月进行标记等等,这些需要特征的衍生,则需要结合具体业务情况来进行判断。
      但是,如果我们判断新衍生的特征在对数据分组的过程中,不同组的数据在标签分布上并没有差别,则分组无效,我们大可不必进行特征衍生。例如,对于一家普通电商平台用户交易时间的秒和分,从业务角度出发,我们很难说每分钟第一秒交易的用户有哪些共同的特点,或者每小时第二分钟交易的用户有哪些共同的特点,甚至是每分钟的前30秒用户有哪些共同特点、每小时的前半个小时用户呈现出哪些共同的特点等,而这类特征就不必在衍生过程中进行创建了。但是,在另外一些场景下,例如某线下超市的周五,可能就是一个需要重点关注的时间,不仅是因为临近周未很多客户会在下班后进行集中采购、而且很多超市有“黑五“打折的习惯,如果是进行超市销售额预测,是否是周五可能就需要单独标注出来,形成独立的一列(该列被包含在dayofweek的衍生列中)。
    注:大多数情况下,关注秒和分钟都是毫无意义的,某些量化以场景除外。
      总结来看,一方面,我们需要从自然周期和业务周期两个角度进行尽可能多的特征衍生,来提供更多的备选数据分组依据来辅助模型建模,另一方面,我们需要结合当前实际业务情况来判断哪些时序特征的衍生特征是有效的,提前规避掉一些可能并无太大用处的衍生特征。
  • 关键时间点的时间差值衍生
      实际操作过程中需要首先人工设置关键时间点,然后计算每条记录和关键时间点之间的时间差,具体时间差的衡量以天和月为主,当然也可以根据数据集整体的时间跨度为前提进行考虑(例如数据集整体时间记录都是一天当中的不同时间,则时间差的计算可以是小时甚至是分钟)。其中,关键时间点一般来说可以是数据集记录的起始时间、结束时间、距今时间,也可以是根据是根据业务或者数据集本身的数据规律,推导出来的关键时间点。
p1 = '2022-01-03;02:31:52'
ts = pd.Timestamp(p) # 创建时间戳
df['timeDiff'] = df['time'] - ts # 时间戳之间的运算
df['dayDiff'] = df['timeDiff'].dt.days / seconds # 相差的天 / 秒数
df['monthDiff'] = np.round(df['dayDiff'] / 30).astype('int') # 相差的月份
df['time_diff_h'] = df['dayDiff'].values.astype('timedelta64[h]') # s / h
# 时间特征的最大/小时间戳
df['time'] = max() / min()
# 当前时间戳
datetime.datetime.now()
pd.Timestamp(datetime.datetime.now())
pd.Timestamp(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) # 控制精度
  • 时序特征衍生封装函数
def timeSeriesCreation(timeSeries, timestamp = None, precision_high=False):
    '''
    时序字段的特征衍生
    : param timeSeries: 时序特征,Series
    : param timeStamp: 默认只计算起始、截止、距今时间的时间差特征衍生,额外关键时间戳需要字典作为输入,key为关键时间戳描述,value为关键时间戳
    : param precision_high: False 精确到年月日 True 精确到时分秒
    : return features_new,colNames_new: 返回创建的新特征矩阵和特征名称
    '''
    # 创建衍生特征df
    features_new = pd.DataFrame()
    # 提取时间字段及时间字段的名称
    timeSeries = pd.to_datetime(timeSeries)
    colNames = timeSeries.name
    # 年月日信息提取
    features_new[colNames + '_year'] = timeSeries.dt.year
    features_new[colNames + '_month'] = timeSeries.dt.month
    features_new[colNames + '_day'] = timeSeries.dt.day
    if precision_high != False:
        features_new[colNames + '_hour'] = timeSeries.dt.hour
        features_new[colNames + '_minute'] = timeSeries.dt.minute
        features_new[colNames + '_second'] = timeSeries.dt.second
    # 自然周期提取
    features_new[colNames + '_quarter'] = timeSeries.dt.quarter
    features_new[colNames + '_weekofyear'] = timeSeries.dt.weekofyear
    features_new[colNames + '_dayofweek'] = timeSeries.dt.dayofweek + 1
    features_new[colNames + '_weekend'] = (features_new[colNames + '_dayofweek'] > 5).astype('int')
    if precision_high != False:
        features_new['hour_section'] = (features_new[colNames + '_hour'] // 6).astype(int) + 1 # 当前小时所属一天的周期,凌晨、上午、下午、晚上,周期以6小时为划分依据
    # 关键时间点时间差计算
    # 创建关键时间戳名称的列表和时间戳列表
    timestamp_name_l = []
    timestamp_l = []
    if timestamp != None: 
        timestamp_name_l = list(timestamp.keys())
        timestamp_l = [pd.Timestamp(x) for x in list(timestamp.values())]
    # 准备通用的关键时间点时间戳, 最大/最小/当前时间戳
    time_max = timeSeries.max() 
    time_min = timeSeries.min()
    time_now = pd.to_datetime(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) # 可以设置精度
    timestamp_name_l.extend(['time_max', 'time_min', 'time_now'])
    timestamp_l.extend([time_max, time_min, time_now])
    # 时间特征衍生
    for timestamp, timestampName in zip(timestamp_l, timestamp_name_l):
        time_diff = timeSeries - timestamp # 标准时间戳
        features_new['time_diff_days' + '_' + timestampName] = time_diff.dt.days
        features_new['time_diff_months' + '_' + timestampName] = np.round(features_new['time_diff_days' + '_' + timestampName] / 30).astype('int')
        if precision_high != False: 
            features_new['time_diff_seconds' + '_' + timestampName] = time_diff.dt.seconds
            features_new['time_diff_h' + '_' + timestampName] = time_diff.values.astype('timedelta64[h]').astype('int')
            features_new['time_diff_s' + '_' + timestampName] = time_diff.values.astype('timedelta64[s]').astype('int')
    colNames_new = list(features_new.columns)
    return features_new, colNames_new
  • 测试代码:
import pandas as pd
import numpy as np
import datetime
t = pd.DataFrame()
t['time'] = ['2022-01-03;02:31:52', 
             '2022-07-01;14:22:01', 
             '2022-08-22;08:02:31', 
             '2022-04-30;11:41:31', 
             '2022-05-02;22:01:27']
timestamp = {'p1':'2022-03-25 23:21:52','p2':'2022-02-15 08:51:02'}
features_new, colNames_new = timeSeriesCreation(timeSeries = t['time'], timestamp = timestamp, precision_high = True)
  • 时序特征的二阶段衍生
      时序字段的二阶段衍生,指的是在衍生的时序字段基础上,再次进行特征衍生。不过一般来说,衍生时序字段彼此之间的交叉衍生往往意义不大,例如月份和时间交叉组合衍生,其效果就和以起止日期作为关键时间点计算月份差值效果相同。因此,时序衍生字段的二阶衍生,往往是结合其他字段共同进行,并且,由于时序衍生字段往往变量取值较多,因此往往以分组统计汇总为主,即以时序衍生字段为keycol,对其他字段进行分组汇总统计,往往能起到较好的效果

你可能感兴趣的:(python,数据挖掘,深度学习,数据分析)