拓冰建站拓冰建站
首页 / 资讯中心 / 正文

机器学习特征工程:Python中日期时间变量处理全指南

在我的机器学习日常数据处理流程中日期和时间变量往往是最容易被忽视、却又最能影响模型效果的一类特征。很多初学者在拿到一份带时间戳的数据后要么直接把日期列删掉要么把它当成普通字符串丢给模型结果白白丢掉了大量有价值的信息。本文是“100天机器学习”系列第34天的内容围绕日期和时间变量的处理展开结合 CampusX 课程中强调的特征工程思路系统讲解 Python 中处理时间数据的完整方法。无论你是刚入门机器学习、正在做数据分析竞赛还是在准备机器学习期末考试这篇文章都能帮你把时间变量这块短板补上。1. 为什么要专门处理日期和时间变量1.1 时间数据到底特殊在哪里先来看一个简单的问题Excel 里有一个订单日期列值为2024-11-15 08:32:17你应该怎么把它喂给模型有人会说转成数字不就行了。确实Python 里可以用datetime或者pd.Timestamp表示时间底层就是一个数字。但这个数字背后包含的信息量非常大它是星期几是月初还是月末是上午还是下午距离某个节日还有几天这些信息如果只是转成“从1970年1月1日算起的秒数”模型只能感知到一个单调递增的数值很难从中捕捉到周期性规律。时间变量的特殊之处在于天然有序性时间数据本身带有先后顺序这决定了它适合提取趋势类特征。周期性一天24小时循环、一周7天循环、一年12个月循环周期特征无法用普通数值直接表达。层级结构年、月、日、时、分、秒是天然的分层关系每一层都可能对预测目标有影响。外部关联性时间与节假日、季节、业务活动强相关单独看一个时间点没有意义需要结合业务背景解读。1.2 机器学习项目中时间变量的典型应用场景在实际项目中时间特征几乎无处不在。以几个典型场景为例场景一电商销售预测。订单时间里的“小时”决定了流量高峰“星期几”决定了周末效应“月份”决定了季节性大促影响。如果把这些字段拆成星期、小时、是否周末模型的预测精度会有明显提升。场景二用户行为分析。用户登录时间、浏览时长、点击时间间隔这些时间戳经过差分处理后可以变成“用户在页面停留了多久”“距上次登录间隔多久”等高价值特征。场景三故障预测与运维监控。服务器日志时间戳可以用来提取“距上次重启间隔”“每小时请求量变化趋势”等特征帮助模型理解系统状态随时间的变化。场景四金融风控。交易时间在深夜还是白天、是工作日还是节假日这类信息对识别异常交易很有帮助。换句话说日期和时间变量不是“要不要处理”的问题而是“怎么处理才能不浪费信息”的问题。1.3 关于 100 天机器学习系列本文内容对应 CampusX 的 100 天机器学习系列第 34 天该系列的特点是每一篇都聚焦一个具体的机器学习知识点从理论到代码完整呈现。日期时间变量处理是特征工程中非常实用的一环虽然这个概念本身不难但真正熟练掌握 string 转换、datetime 运算、pandas 时间序列索引和周期性特征提取需要一段时间的刻意练习。2. 环境准备与版本说明2.1 运行环境本文示例代码以 Python 3.9 环境为基础使用到的核心库包括pandas负责时间数据的读取、转换和特征提取。numpy用于日期差值和数值计算。datetimePython 标准库处理基础日期时间对象。matplotlib用于可视化时间序列数据选学。由于不同版本的 pandas 在时间解析接口上略有差异建议使用较新的稳定版本。在命令行执行以下命令查看当前版本python -c import pandas as pd; print(pd.__version__)如果尚未安装相关依赖可以使用 pip 统一安装pip install pandas numpy matplotlib2.2 示例数据说明为了便于讲解本文不使用外部数据集而是直接在代码中构造一份模拟数据。这份数据模拟了某电商平台 2024 年 11 月的订单记录包含订单 ID、下单时间、支付金额和用户 ID 四个字段。数据本身不涉及真实业务重点演示处理思路。3. Python 中表示时间的基本方式在动手做特征工程之前需要先搞清楚 Python 中处理时间数据的三种常见形式因为很多报错都源于这三者之间的混淆。3.1 datetime 模块datetime是 Python 内置模块其中最常用的是datetime.datetime类。它表示一个具体的日期和时间例如from datetime import datetime now datetime.now() print(now) print(type(now))输出结果类似2024-11-20 15:08:32.123456 class datetime.datetimedatetime对象可以直接访问year、month、day、hour、minute、second等属性这些属性在特征提取时非常方便。3.2 pandas 的 Timestamppandas提供了自己的时间标量类型Timestamp它与datetime对象大部分操作兼容但功能更强。例如import pandas as pd ts pd.Timestamp(2024-11-15 08:32:17) print(ts) print(ts.day_name()) # 输出 Friday print(ts.week) # 输出第几周Timestamp的优势在于可以无缝接入 DataFrame并且自带很多日期相关的方法比如day_name()可以直接得到星期几的英文名称。3.3 三种形式的转换关系import datetime 模块、pandas Timestamp、字符串这三者之间经常需要互相转换。核心的转换关系如下from datetime import datetime import pandas as pd # 字符串转 datetime dt_obj datetime.strptime(2024-11-15 08:32:17, %Y-%m-%d %H:%M:%S) # datetime 转 Timestamp ts pd.Timestamp(dt_obj) # Timestamp 转字符串 str_time ts.strftime(%Y-%m-%d) # 字符串直接转 Timestamp推荐 ts2 pd.to_datetime(2024-11-15 08:32:17)这里最常用、也最推荐的方式是pd.to_datetime()它对各种常见格式的解析能力非常强甚至可以自动识别带时区、带逗号、带斜杠的日期字符串。4. 日期时间特征提取完整实战下面开始进入本文的核心环节用一份完整的示例数据演示如何把原始时间戳变成可供机器学习模型使用的特征。4.1 创建项目结构与模拟数据先创建一个工作目录比如ml_time_features/在目录下新建一个 Python 文件time_feature_engineering.py。后续所有代码均写入该文件。首先构造模拟数据# 文件路径ml_time_features/time_feature_engineering.py import pandas as pd import numpy as np # 构造 2024 年 11 月的模拟订单数据 np.random.seed(42) base_time pd.date_range(start2024-11-01, end2024-11-30 23:00:00, freqh) order_count len(base_time) data pd.DataFrame({ order_id: [fORD_{i:05d} for i in range(order_count)], order_time: base_time, amount: np.random.uniform(20, 1000, order_count).round(2), user_id: np.random.randint(1000, 2000, order_count) })pd.date_range按小时生成 2024 年 11 月整月的时间戳共 720 条模拟订单。运行后可以使用data.head()查看数据前几行。4.2 把字符串转换为标准时间类型真实项目中从数据库导出的时间字段通常是字符串。我们需要先把它们转换为 pandas 的datetime64类型才能进行后续操作。# 模拟原始数据中的时间字段为字符串 data[order_time_str] data[order_time].astype(str) # 转换为 datetime64 data[order_time] pd.to_datetime(data[order_time_str])转换完成后可以用data.dtypes检查字段类型order_time应该显示为datetime64[ns]。这一步是整个时间特征工程的基石如果类型不对后续所有属性提取都会报错。有几个常用参数需要特别注意format手动指定解析格式例如format%Y-%m-%d %H:%M:%S。当数据量很大时指定格式可以显著提升解析速度。errors解析失败时的处理方式可选raise默认抛异常、coerce解析失败置为 NaT、ignore返回原对象。utc是否将数据解析为 UTC 时间。当数据量达到百万行级别时建议使用format参数加快速度否则 pandas 会尝试推断格式性能开销较大。4.3 提取基础时间属性将时间列转换为标准类型后就可以通过.dt访问器提取各种时间属性。下面把常用属性一次性提取出来# 提取基础时间属性 data[year] data[order_time].dt.year data[month] data[order_time].dt.month data[day] data[order_time].dt.day data[hour] data[order_time].dt.hour data[minute] data[order_time].dt.minute data[second] data[order_time].dt.second data[weekday] data[order_time].dt.weekday # 周一为0周日为6 data[day_of_year] data[order_time].dt.dayofyear data[week_of_year] data[order_time].dt.isocalendar().week.astype(int) data[quarter] data[order_time].dt.quarter这些属性分为几类时间粒度信息year、month、day、hour、minute、second对应时间戳上不同层级的粒度。周期位置信息weekday、day_of_year、week_of_year、quarter用于识别周期性规律。还有两个稍复杂但很有用的属性是weekday_name和is_month_enddata[weekday_name] data[order_time].dt.day_name() data[is_month_start] data[order_time].dt.is_month_start data[is_month_end] data[order_time].dt.is_month_end data[is_leap_year] data[order_time].dt.is_leap_yearis_month_start和is_month_end返回布尔值在金融、销售场景中非常有用因为月末月初往往伴随着特殊的业务节奏。4.4 构造布尔型业务特征除了直接提取时间属性更重要的是结合业务构造有意义的布尔特征。仍然以电商订单为例# 是否周末 data[is_weekend] data[order_time].dt.weekday 5 # 是否工作日 data[is_workday] data[is_weekend] False # 是否夜间订单22点到次日6点 hour data[order_time].dt.hour data[is_night] ((hour 22) | (hour 6)) # 是否高峰时段10点到12点14点到16点 data[is_rush_hour] (((hour 10) (hour 12)) | ((hour 14) (hour 16))) # 是否月初1号到5号 data[is_month_begin] data[order_time].dt.day 5 # 是否临近双十一11月1日到11月11日 data[is_near_double_11] (data[order_time].dt.month 11) (data[order_time].dt.day 11)布尔特征本质上就是 0/1 数值特征可以直接输入模型。这类特征把“时间点”转换成“业务状态”对算法非常友好。以is_night为例深夜订单可能与白天订单在金额、品类上存在明显差异模型识别出这个状态后就能在不同时间段学习不同规律。4.5 时间差与滞后特征在很多实际问题中当前时间点的绝对日期不重要重要的是“距离上一个事件过去了多久”。例如用户在某个页面停留了多久。客服距上次响应订单的时间间隔。服务器距离上次宕机的时间。两次购买行为之间的间隔。这些特征统称为时间差特征。计算方式非常简单# 计算相邻两笔订单之间的时间间隔分钟 data[prev_order_time] data[order_time].shift(1) data[time_diff_minutes] (data[order_time] - data[prev_order_time]).dt.total_seconds() / 60 # 删除无意义的首行第一行没有前一条记录 data data.dropna(subset[time_diff_minutes])time_diff_minutes表示用户下相邻两笔订单之间的间隔可用于衡量订单集中度或用户活跃程度。同样的思路可以推广到“距上次登录”“距上次购买”“距上次故障”等场景。要特别注意DataFrame.shift()的用法shift(1)表示把整列往下移动一行这样同一行左侧就是“上一条记录的时间”。这个操作本质上是把时间序列变成监督学习样本的关键步骤。另外也可以计算固定参照时间的差值# 距离本月初的天数 month_start data[order_time] - pd.offsets.MonthBegin() data[days_since_month_start] (data[order_time] - month_start).dt.days4.6 周期性编码时间特征最特殊的地方在于周期性。比如hour特征23 点和 0 点虽然在数值上相差很远但它们在业务上几乎属于同一个时段夜间。如果直接把 23 和 0 作为数值特征输入模型模型会认为它们是两个完全不同的时间点丢失了“循环”的含义。解决周期性问题的经典方案是三角函数编码# 小时周期编码 data[hour_sin] np.sin(2 * np.pi * data[hour] / 24) data[hour_cos] np.cos(2 * np.pi * data[hour] / 24) # 星期周期编码 data[weekday_sin] np.sin(2 * np.pi * data[weekday] / 7) data[weekday_cos] np.cos(2 * np.pi * data[weekday] / 7) # 月份周期编码 data[month_sin] np.sin(2 * np.pi * data[month] / 12) data[month_cos] np.cos(2 * np.pi * data[month] / 12)以小时为例hour_sin和hour_cos是一对互补的数值它们的组合没有大小顺序而是把 24 个小时映射到一个平面圆上。0 点和 23 点在圆上的位置非常接近模型也就更容易学到“这两个时段行为相似”。这种编码方式适用于一切存在循环周期的特征。如果项目使用树模型XGBoost、LightGBM、随机森林直接保留原始 hour、weekday 数值通常影响不大因为树模型可以搜索最优切分点但如果使用线性模型、神经网络、KNN建议优先使用三角函数编码。4.7 运行与验证把以上代码整合到完整脚本中运行python time_feature_engineering.py。最后使用data.head(10)查看结果。预期输出包含以下列order_id order_time amount user_id month day hour weekday is_weekend is_night hour_sin hour_cos ...可以进一步使用描述性统计验证生成的特征是否符合预期print(data[[month, day, hour, weekday, is_weekend, is_night]].describe()) print(data[is_weekend].value_counts())从统计结果中应该能看到is_weekend大约有 2/7 的 Trueis_night大约有 1/3 的 True这些分布符合常识说明特征构造逻辑没有问题。5. 时区问题与规范化处理5.1 为什么时区很重要分布式系统中服务器日志通常记录 UTC 时间而业务数据使用本地时间。如果不做统一处理时间特征会互相冲突。例如北京时间的 2024-11-15 08:00 与 UTC 时间的 2024-11-15 08:00 根本不是同一个时刻。pandas 提供了时区处理方法# 假设原始时间为 UTC data[order_time_utc] data[order_time].dt.tz_localize(UTC) # 转换为北京时间UTC8 data[order_time_beijing] data[order_time_utc].dt.tz_convert(Asia/Shanghai) # 去除时区信息转为纳秒级时间戳 data[order_time_naive] data[order_time_beijing].dt.tz_localize(None)tz_localize用于给没有时区信息的时间设置时区tz_convert用于在不同时区之间转换。在特征提取之前务必确认所有时间列处于同一时区。5.2 国际化项目的建议对于用户遍布全球的分析任务比较稳妥的做法是在数据管道入口统一把时间转换为 UTC 存储。在特征提取阶段按业务需要转换到目标时区。同时保留原始时区标识方便回溯。如果项目只需要本地时间特征可以省略时区处理但如果数据来自多个地域统一时区是避免隐性错误的关键。6. 常见问题与排查思路日期时间变量处理虽然概念简单实际编码时却很容易踩坑。下面整理几个高频问题。问题现象常见原因解决思路pd.to_datetime抛ParserError字符串格式混杂存在无法自动识别的格式使用errorscoerce先定位非法值或指定format参数提取hour属性时报AttributeError列类型还是字符串未转换为datetime64先执行pd.to_datetime()并用dtypes确认类型时间差列为空值使用了shift()导致首行为 NaN使用dropna()删除首行或用fillna(0)填充绘制日期列趋势图时报错日期列被当作 object 类型传给 matplotlib转换为datetime64类型后再绘图小时特征影响模型效果不明显直接使用数值特征未体现周期性考虑使用sin/cos周期性编码多源数据时间对不上时区未统一或者日期格式不一致在入口统一 UTC指定标准格式解析针对第一个问题补充一种快速定位非法日期字符串的方法# 解析失败时保留原始字符串 invalid_mask pd.to_datetime(data[order_time_str], errorscoerce, format%Y-%m-%d %H:%M:%S).isna() invalid_samples data.loc[invalid_mask, order_time_str] print(f解析失败 {len(invalid_samples)} 行示例) print(invalid_samples.head())这种方法在清洗真实脏数据时非常实用可以快速定位是哪几条记录导致解析失败。7. 最佳实践与工程建议7.1 把时间特征工程模块化在处理多个项目时建议把日期时间特征提取封装成独立函数方便复用。以下是一个基础封装示例def extract_time_features(df, time_col): 从时间列提取常用特征并返回新增特征列。 ts pd.to_datetime(df[time_col]) df df.copy() df[year] ts.dt.year df[month] ts.dt.month df[day] ts.dt.day df[hour] ts.dt.hour df[weekday] ts.dt.weekday df[is_weekend] ts.dt.weekday 5 df[quarter] ts.dt.quarter # 周期性编码 df[hour_sin] np.sin(2 * np.pi * df[hour] / 24) df[hour_cos] np.cos(2 * np.pi * df[hour] / 24) df[weekday_sin] np.sin(2 * np.pi * df[weekday] / 7) df[weekday_cos] np.cos(2 * np.pi * df[weekday] / 7) return df这样在训练集和测试集上可以复用同一套逻辑避免训练测试特征不一致的问题。7.2 结合业务定义特征不盲目堆砌初学者容易犯的一个错误是把所有能提取的时间属性全部提取出来生成上百个时间特征。这种做法带来的问题不是信息太多而是噪声太多。特征工程的本质是“用业务理解筛选有用信息”不是“把所有字段都转成数字”。构造每个时间特征之前先问自己一个问题这个特征对预测目标可能有影响吗例如预测某个时刻的交通流量时“星期几”和“小时”非常重要但预测商品价格时这些特征可能毫无作用。时间特征选择必须回到业务场景本身。7.3 训练集与测试集的时间一致性时间序列建模中最容易犯的错是数据泄漏。典型场景包括用未来数据计算滑动平均特征然后在预测过去时使用该特征。对全量数据做归一化后再切分训练集和测试集。在特征提取阶段使用了包含全局统计量的时间字段。对于时间序列任务正确的做法是严格按照时间顺序划分数据集并且在特征提取时保证训练集和测试集分开计算。例如“距上次订单间隔”在训练集上可以通过shift()计算但在测试集上必须用训练集末尾的数据作为第一条测试记录的“上次时间”否则会出现信息泄漏。7.4 特征存储与命名规范建议对时间特征采用统一命名规则例如*_sin/*_cos周期性编码特征。is_*布尔特征。time_diff_*时间差特征。*_lag滞后特征。days_since_*/hours_since_*距离参照时间的时间间隔。统一命名不仅方便团队协作也方便在模型特征重要性分析中快速定位特征来源。7.5 使用 dateutil 解析复杂字符串当时间字符串格式非常复杂、pd.to_datetime无法识别时可以考虑使用第三方库dateutilfrom dateutil.parser import parse s November 15, 2024 08:32:17 AM dt_obj parse(s) print(dt_obj)但需要注意dateutil.parser.parse的推断能力虽然强但解析速度较慢不适合在百万行级数据上直接使用。实际项目中优先使用pd.to_datetime并指定format。8. 日期时间变量的进阶学习方向掌握了本文的基础内容后可以继续往以下方向深入1. 时间序列重采样。pandas 的resample()方法可以把分钟级数据聚合成小时级、天级数据在构造聚合特征时非常有用。# 按天统计订单总额 daily_amount data.set_index(order_time)[amount].resample(D).sum()2. 滑动窗口特征。滚动平均值、滚动标准差可以有效捕捉近期趋势但要注意窗口大小与业务周期匹配。3. 节假日特征。中国法定节假日和调休安排会显著影响电商、交通、旅游等行业的数据模式可以引入外部节假日日历作为特征。4. 时间序列模型。如果预测目标是连续的时序值可以进一步学习 ARIMA、Prophet、LSTM 等时间序列专用模型它们对时间结构的假设与普通回归模型不同。5. 特征选择与重要性分析。使用树模型的特征重要性或 SHAP 值验证你构造的时间特征是否真正对模型预测有帮助及时删除无效特征保持特征集简洁。如果正在备考机器学习期末考试或者正在做数据竞赛建议重点掌握 4.2 到 4.6 节中的操作这些都是高频考点和比赛常用技巧。尤其是pd.to_datetime、.dt访问器、shift()和三角函数周期编码这几项几乎每次处理时间序列数据都会用到。最后日期时间变量处理是一项“看起来简单、做好不容易”的工作。在理解业务的基础上提取合适的特征保持代码模块化、可复用并始终警惕数据泄漏和时区不一致问题你的机器学习模型才能从时间数据中真正获益。如果你在实践中遇到其他时间变量处理的问题欢迎自己动手构造数据多练几遍这部分能力的提升对后续学习时间序列分析和特征工程都大有帮助。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门