查看全课大纲(第 17 / 35 节)
- 1.特征工程简介
- 2.特征工程的处理流程
- 3.数据与特征概述
- 4.数据统计、相似度与数据探索
- 5.数据探索工具 Pandas_profiling
- 6.工具应用:《泰坦尼克》分析报告
- 7.构造一个微型链家租房数据集用于演示
- 8.数据预处理综述
- 9.缺失值处理实操
- 10.异常值处理实操
- 11.连续值特征构建
- 12.类别型数据特征构建
- 13.特征幅度缩放
- 14.特征离散化
- 15.特征编码:让类别数据参与建模
- 16.特征变换
- 17.时间特征
- 18.特征衍生
- 19.自动化特征工具
- 20.特征选择综述
- 21.特征选择方法
- 22.过滤式特征选择方法实操
- 23.包裹式特征选择方法
- 24.嵌入式特征选择
- 25.混序法与混合法特征选择
- 26.特征降维综述
- 27.案例实操:特征降维
- 28.特征监控概述与指标
- 29.Iris 数据集特征漂移检测案例
- 30.特征监控实战:用分布对比检测数据漂移
- 31.实时特征监控
- 32.电商销量预测项目背景介绍
- 33.数据预处理和特征工程(一)
- 34.数据预处理和特征工程(二)
- 35.数据建模
时间特征
约 7 分钟
小象实战讲义 · 特征工程精讲与实践
在数据科学项目中,时间信息是蕴含丰富业务规律的“富矿”。无论是电商销量、用户活跃度还是设备运行状态,其变化往往与时间周期紧密相关。本节我们将系统学习如何从原始的日期/时间字符串中,抽取有意义的特征,例如月份、星期几、是否为周末、距离特定日期的间隔等,从而让模型能够捕捉到时间维度的模式。
💡 核心导读
- 时间特征的价值:理解时间型数据(如日期、时间戳)在刻画业务周期、趋势和间隔方面的核心作用。
- 类型转换是第一步:掌握使用
pandas.to_datetime()将字符串转换为日期时间类型,这是所有后续操作的基础。 - 关键信息抽取:学习通过
.dt访问器轻松提取年、月、日、星期、季度、一年中的第几天等结构化特征。 - 时间间隔计算:学会计算两个日期之间的差值,生成如“距离某个促销日的天数”等连续值特征。
时间型数据特征工程概述
时间型数据,如 2021-12-25、2023-08-15 14:30:00 等,在原始数据集中通常以字符串(object 类型)存储。为了从中挖掘信息,我们需要将其转换为程序能够理解的日期时间类型,进而抽取对预测目标有影响的维度。
根据课件,时间特征主要分为三类:
- 日期型数据特征:如年、月、日、星期几、季度、一年中的第几周等。
- 一天中的时间特征:如小时、分钟、所属时间段(上午、下午、夜晚)等。
- 时间间隔特征:计算两个时间点之间的差值,常用于用户行为序列分析,如“距离上一次购买的天数”、“平均活跃间隔”等。
本节我们将通过一个汽车销量数据集,演示如何完成从类型转换到特征构建的完整流程。
实战:汽车销量数据的时间特征构建
本节我们将使用一份模拟的汽车月度销量数据。在真实工业场景中,你拿到的原始数据很可能就是一个日期字符串列和一个数值列。我们的目标是基于日期字符串,构造出更多有助于销量预测的时间特征。
步骤一:数据读取与审视
首先,我们创建一份最小化的示例数据来模拟 car_data.csv 的形态。这份数据包含日期字符串和对应的销量计数。
import pandas as pd
import numpy as np
# 构造一份小规模示例数据,模拟汽车月度销量
data = {
'date_t': ['2021-01-15', '2021-02-20', '2021-03-10', '2021-04-05', '2021-05-25',
'2021-06-18', '2021-07-22', '2021-08-30', '2021-09-12', '2021-10-08'],
'count': [125.0, 138.0, 119.0, 156.0, 167.0, 145.0, 178.0, 190.0, 132.0, 148.0]
}
car_sales = pd.DataFrame(data)
print("原始数据预览:")
print(car_sales)
print("\n数据信息 (info):")
print(car_sales.info())原始数据预览:
date_t count
0 2021-01-15 125.0
1 2021-02-20 138.0
2 2021-03-10 119.0
3 2021-04-05 156.0
4 2021-05-25 167.0
5 2021-06-18 145.0
6 2021-07-22 178.0
7 2021-08-30 190.0
8 2021-09-12 132.0
9 2021-10-08 148.0
数据信息 (info):
<class 'pandas.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date_t 10 non-null str
1 count 10 non-null float64
dtypes: float64(1), str(1)
memory usage: 292.0 bytes
None从 info 可以看出,date_t 字段是字符串类型(pandas 3.0 中显示为 str;旧版本 pandas 显示为 object,原课程录制时即为 object),而 count 是数值型 float64。字符串类型的日期无法直接进行时间运算,因此下一步必须进行类型转换。
步骤二:日期时间类型转换
使用 pandas.to_datetime() 函数,我们可以轻松地将格式规范的日期字符串列转换为 datetime64 类型。转换后,pandas 会识别其日期时间属性,并允许我们通过 .dt 访问器提取各部分信息。
import pandas as pd
# 重新构造数据,确保本代码块自包含
data = {
'date_t': ['2021-01-15', '2021-02-20', '2021-03-10', '2021-04-05', '2021-05-25',
'2021-06-18', '2021-07-22', '2021-08-30', '2021-09-12', '2021-10-08'],
'count': [125.0, 138.0, 119.0, 156.0, 167.0, 145.0, 178.0, 190.0, 132.0, 148.0]
}
car_sales = pd.DataFrame(data)
# 核心操作:将字符串列转换为 datetime64 类型,并创建新列 ‘date’
car_sales['date'] = pd.to_datetime(car_sales['date_t'])
print("转换后数据信息:")
print(car_sales.info())
print("\n转换后数据预览(注意新增的 date 列,其显示与 date_t 相同,但内部类型已变):")
print(car_sales[['date_t', 'date', 'count']])转换后数据信息:
<class 'pandas.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date_t 10 non-null str
1 count 10 non-null float64
2 date 10 non-null datetime64[us]
dtypes: datetime64[us](1), float64(1), str(1)
memory usage: 372.0 bytes
None
转换后数据预览(注意新增的 date 列,其显示与 date_t 相同,但内部类型已变):
date_t date count
0 2021-01-15 2021-01-15 125.0
1 2021-02-20 2021-02-20 138.0
2 2021-03-10 2021-03-10 119.0
3 2021-04-05 2021-04-05 156.0
4 2021-05-25 2021-05-25 167.0
5 2021-06-18 2021-06-18 145.0
6 2021-07-22 2021-07-22 178.0
7 2021-08-30 2021-08-30 190.0
8 2021-09-12 2021-09-12 132.0
9 2021-10-08 2021-10-08 148.0现在,date 列的类型是 datetime64[us](新版本 pandas 的默认精度;旧版本默认为 ns,对本节的特征抽取没有任何影响)。虽然打印出来和 date_t 看起来一样,但它已经具备了时间内涵,我们可以从中“拆解”出大量特征。
步骤三:抽取关键时间维度特征
转换成功后,通过 .dt 访问器,我们可以像访问对象属性一样,提取日期时间的各个组成部分。这是构建时间特征最常用、最直接的方法。
import pandas as pd
from datetime import datetime
# 重新构造包含日期时间类型列的数据
data = {
'date_t': ['2021-01-15', '2021-02-20', '2021-03-10', '2021-04-05', '2021-05-25',
'2021-06-18', '2021-07-22', '2021-08-30', '2021-09-12', '2021-10-08'],
'count': [125.0, 138.0, 119.0, 156.0, 167.0, 145.0, 178.0, 190.0, 132.0, 148.0]
}
car_sales = pd.DataFrame(data)
car_sales['date'] = pd.to_datetime(car_sales['date_t'])
# 1. 抽取月份 (1-12)
car_sales['month'] = car_sales['date'].dt.month
# 2. 抽取日期 (几号,1-31)
car_sales['day_of_month'] = car_sales['date'].dt.day
# 3. 抽取一年中的第几天 (1-365/366)
car_sales['day_of_year'] = car_sales['date'].dt.dayofyear
# 4. 抽取星期几 (0=Monday, 6=Sunday)
car_sales['day_of_week'] = car_sales['date'].dt.dayofweek
# 5. 基于星期几判断是否为周末 (这里定义周六、日为周末)
car_sales['is_weekend'] = car_sales['day_of_week'].apply(lambda x: 1 if x >= 5 else 0)
# 6. 计算时间间隔:以2021年12月25日为基准,计算每个日期距离基准的天数
reference_date = datetime(2021, 12, 25)
car_sales['days_to_ref'] = (reference_date - car_sales['date']).dt.days
print("完成特征构建后的数据:")
print(car_sales[['date_t', 'month', 'day_of_month', 'day_of_year', 'day_of_week', 'is_weekend', 'days_to_ref', 'count']])完成特征构建后的数据:
date_t month day_of_month ... is_weekend days_to_ref count
0 2021-01-15 1 15 ... 0 344 125.0
1 2021-02-20 2 20 ... 1 308 138.0
2 2021-03-10 3 10 ... 0 290 119.0
3 2021-04-05 4 5 ... 0 264 156.0
4 2021-05-25 5 25 ... 0 214 167.0
5 2021-06-18 6 18 ... 0 190 145.0
6 2021-07-22 7 22 ... 0 156 178.0
7 2021-08-30 8 30 ... 0 117 190.0
8 2021-09-12 9 12 ... 1 104 132.0
9 2021-10-08 10 8 ... 0 78 148.0
[10 rows x 8 columns]代码解读与 .dt 访问器:
.dt.month,.dt.day,.dt.dayofyear,.dt.dayofweek等都是datetime64系列列的内置属性,直接返回整数。day_of_week返回 0-6,代表周一到周日。这是一种序号编码(Label Encoding)。is_weekend是我们根据业务逻辑(定义周六、日为周末)衍生出的二值特征(0/1),这是一个简单的特征衍生过程。days_to_ref展示了如何计算时间间隔。两个datetime对象相减,得到一个Timedelta对象,再通过.dt.days提取整数天数。这种“距离某个关键日期(如促销日、节假日前)的天数”是预测模型中非常有效的特征。
通过以上几步,我们成功地将一个原始的日期字符串列,扩展成了包含月份、月中日期、年中日期、星期类型、周末标志以及特定时间间隔在内的多个特征,极大地丰富了模型的信息输入。
📝 动手练一练
- 概念辨析:在时间特征工程中,为什么必须先将
object类型的日期字符串转换为datetime64类型?如果跳过这一步直接对字符串进行操作,可能会遇到哪些问题?
👉 点击查看参考答案
参考答案: 必须进行类型转换是因为:
- 语义识别:
datetime64类型让 pandas 和后续机器学习库能够理解该列数据代表时间,从而支持基于时间的排序、切片和运算。 - 特征抽取:只有转换后,才能使用
.dt访问器方便地提取年、月、日、小时、星期等结构化成分。对字符串操作需要复杂的字符串解析(如切片、正则匹配),容易出错且不通用。 - 时间运算:计算间隔、比较先后等操作只能在时间类型上进行。字符串无法直接进行减法等运算。
- 性能与一致性:pandas 对
datetime64类型有优化,处理效率更高,且能自动处理闰年、时区等复杂情况,保证结果一致性。
- 动手实践:假设你有一份用户登录记录,包含登录时间戳
login_time(格式如2023-08-15 14:30:00)。请编写代码,从中提取出“小时”(hour)和“是否属于工作时间”(is_work_hour,假设工作时间为 9:00-18:00)两个特征。
👉 点击查看参考答案
参考答案:
import pandas as pd
# 示例用户登录数据
login_data = {
'user_id': [1, 2, 3, 4, 5],
'login_time_str': ['2023-08-15 09:15:00', '2023-08-15 14:30:00',
'2023-08-15 20:45:00', '2023-08-16 10:00:00', '2023-08-16 18:30:00']
}
df_login = pd.DataFrame(login_data)
# 1. 类型转换
df_login['login_time'] = pd.to_datetime(df_login['login_time_str'])
# 2. 抽取小时
df_login['hour'] = df_login['login_time'].dt.hour
# 3. 判断是否为工作时间 (9点至18点之间,包含9点不包含18点)
df_login['is_work_hour'] = df_login['hour'].apply(lambda h: 1 if 9 <= h < 18 else 0)
print(df_login[['user_id', 'login_time_str', 'hour', 'is_work_hour']])user_id login_time_str hour is_work_hour
0 1 2023-08-15 09:15:00 9 1
1 2 2023-08-15 14:30:00 14 1
2 3 2023-08-15 20:45:00 20 0
3 4 2023-08-16 10:00:00 10 1
4 5 2023-08-16 18:30:00 18 0本章小结
本节我们深入学习了时间型数据的特征工程方法。时间特征的核心在于从单一的时间点信息中,解构出对业务有解释力的多个维度。
📋 行动清单
- 掌握类型转换:面对日期/时间字符串,第一反应应是使用
pd.to_datetime()进行转换。 - 熟练使用
.dt访问器:记住常用属性如.month,.day,.dayofweek,.hour等,并能快速从日期时间列中抽取这些特征。 - 思考业务场景:在构建时间特征时,始终联系业务。例如,对于销量预测,月份、星期几、是否节假日、距离大促日天数可能都是关键特征;对于用户行为分析,一天中的时段、操作间隔频率则更为重要。
—— 小象教研组