
Polars 时间序列解析完全指南时间数据类型、CSV 日期推断与字符串日期转换【免费下载链接】polarsExtremely fast Query Engine for DataFrames, written in Rust项目地址: https://gitcode.com/GitHub_Trending/po/polars导读本文聚焦于 Polars 对时间序列数据的原生解析能力系统梳理 Polars 内置的四大时间数据类型Date、Datetime、Duration、Time及其底层表示讲解两条核心解析路径——通过try_parse_dates在读取 CSV 时自动推断日期以及通过str.to_date/str.to_datetime把字符串列精确转换为时间类型最后深入混合 UTC 偏移如夏令时切换场景的解析与时区转换处理。读完本文你将掌握在 Polars 中正确解析、验证与加工时间数据的完整实战方案并理解各参数背后的实现机制。本文内容以官方用户指南 transformations/time-series/parsing.md 为主线骨架并结合仓库内 Python 接口、Rust 表达式层的源码加以印证与扩充。它属于时间序列系列的第一环后续还可衔接 filter.md时间过滤、rolling.md滚动窗口、resampling.md重采样以及专门的 timezones.md时区深度专题。一、Polars 的时间数据类型全景要对时间序列做“更复杂的分组与重采样操作”首先需要把原始数据落到正确的时间类型上。官方文档列出了 Polars 内置的四种时间temporal数据类型数据类型语义内部表示Date纯日期如2014-07-08自 UNIX 纪元以来的天数使用32 位有符号整数编码Datetime日期 时间如2014-07-08 07:00:00自 UNIX 纪元以来的64 位整数可携带不同的时间单位ns/us/msDuration时间差/时间增量类型由Date/Datetime相减产生语义上等价于 Python 的timedelta同样以 64 位整数 时间单位表示Time一天内的时间如07:00:00自午夜以来的纳秒数理解内部表示的价值在于把握三条实际约束Date只有天粒度它不携带时分秒无法表达“某日几点”这类信息需要精确时刻时应使用Datetime。Datetime的时间单位决定精度与范围同一时刻可以用ns/us/ms表达单位的选择会影响整数取值范围与后续计算语义例如dt.epoch的返回粒度。单位本身属于类型的一部分列的类型显示为如datetime[μs]的形式。Duration不是独立的“日期值”它描述的是两个时刻之间经过的时间长度因此在做“日期相减得到天数/小时数”这类操作时需要先想清楚目标单位见 [duration 相关算子]。二、从文件解析日期CSV 的自动类型推断2.1try_parse_datesTrue触发推断读取 CSV 时只要打开try_parse_dates开关Polars 就会在 schema 推断阶段尝试把字符串列识别为日期/时间类型。官方示例读取了仓库中的苹果股票收盘价数据集import polars as pl df pl.read_csv(docs/assets/data/apple_stock.csv, try_parse_datesTrue) print(df)对应数据集位于 docs/assets/data/apple_stock.csv其首行与数据形如Date,Close 1981-02-23,24.62 1981-05-06,27.38 ...开启该开关后Date列会被推断为Date类型而不是字符串完整可运行的示例脚本参见 docs/source/src/python/user-guide/transformations/time-series/parsing.py。2.2 推断发生在哪一步代价是什么官方文档明确指出try_parse_dates会在 schema 推断的样本行上触发日期类型推断样本规模由infer_schema_length参数控制默认 100 行。在 Python 接口中该参数在 py-polars/src/polars/io/csv/functions.py 的函数签名中均有体现read_csv(..., try_parse_dates: bool False, infer_schema_length100, ...)scan_csv(..., try_parse_dates, ...)惰性/流式读取路径同样支持分批读取的read_csv_batched亦在 py-polars/src/polars/io/csv/batched_reader.py 中接收try_parse_dates参数。try_parse_dates的参数说明见 functions.py给出了该开关的能力边界尝试自动解析日期。大多数 ISO8601 风格的格式以及少数其他格式可以被推断如果推断失败该列会保持pl.String类型不变。由此可以提炼出三点实战判断推断是启发式的不是万能的ISO8601 系列如2014-07-08、2014-07-08 07:00:00命中率高但非标准格式如08/07/2014、July 8, 2014很可能推断失败而退化为字符串。此时应改用下文第三节的显式str.to_date方案。schema 推断本身有计算开销文档提醒“Schema inference is computationally expensive”如果调大infer_schema_length例如为了在更大样本上提高推断准确率会明显拖慢文件加载速度。因此官方建议路径是能明确类型就不要依赖推断——要么对 CSV 显式传入schema/schema_overrides要么改用带 schema 的列式格式。失败静默降级推断不成功时不会报错列悄悄保持为String这可能导致后续时序计算出现“类型不匹配”的报错或错误结果。建议在读取后打印df.schema复核列类型。2.3 二进制格式为何无需推断与 CSV 相反Parquet 等二进制列式格式自带 schema每个字段的类型在文件元数据中声明Polars 直接尊重并采用该 schema因此不存在“是否尝试解析日期”的问题也天然避开了推断开销与不确定性。这一点让“数据落地到 Parquet 再反复读取”成为时序数据工作流中的推荐实践。三、把字符串列显式转换为日期str.to_date当 CSV 自动推断不可靠例如列被解析成字符串、或数据来自 API/数据库导出的文本时可以使用字符串命名空间的str.to_date完成精确转换df pl.read_csv(docs/assets/data/apple_stock.csv, try_parse_datesFalse) df df.with_columns(pl.col(Date).str.to_date(%Y-%m-%d)) print(df)这里%Y-%m-%d是日期格式串用于声明源字符串的结构格式串规范与 Pythonstrftime同源完整规范可参考 Rustchronocrate 的 strftime 文档。日期字符串解析虽属文档范围内但它与格式字符串约定相关若日期字符串是明确的 ISO8601 格式、不携带时区或区域歧义也可以直接使用 Rust 语法级联parse_str若格式不符不报错而是输出 null因此更适合先探索、再对列显式解析的工作流。从 Python 接口源码 py-polars/src/polars/expr/string.py 可以看到str.to_date的完整签名与语义def to_date( self, format: str | None None, *, strict: bool True, exact: bool True, cache: bool True, ) - Expr各参数的实战含义参数默认值含义与建议formatNone转换所用格式串置为None时由数据自动推断格式。示例%Y-%m-%d。strictTrue任何一条记录转换失败时是否报错False时失败结果变为null便于先转后清洗。exactTrue是否要求格式与整个字符串精确匹配False时允许格式只在字符串的某个位置匹配。文档与源码均强调exactFalse会引入性能惩罚先行清洗数据几乎总是更优解。cacheTrue是否用“已转换唯一值的缓存”复用转换结果可显著加速重复值多的列。配套的str.to_timeTime、str.to_datetimeDatetime位于同一命名空间其中to_datetime参数更丰富我们将在第五节展开。四、从时间列提取特征.dt命名空间转换完成后就可以用.dt命名空间从时间列中提取年、月、日等特征用于分组、排序与特征工程df_with_year df.with_columns(pl.col(Date).dt.year().alias(year)) print(df_with_year)例如对苹果股价数据会得到新增的year列1981、1982、1983……可进一步配合group_by(year)做逐年聚合。.dt命名空间在 Python 侧注册于 py-polars/src/polars/expr/datetime.py其内部一一映射到 Rust 表达式层 crates/polars-plan/src/dsl/dt.rs 的实现提取日期组成部分dt.year()、dt.month()、dt.quarter()、dt.week()、dt.day()、dt.ordinal_day()、dt.weekday()、dt.hour()等如 dt.rs 中的year/month/day/weekday/hour时间戳换算dt.epoch(time_unit)可把时间列转成对应单位s/ms/us/ns的整型时间戳时区处理dt.convert_time_zone(time_zone)详见第五节时间差处理dt.total_days()、dt.total_hours()等用于把Duration转成目标单位的数值。从源码结构可以看出一个通用模式.dt/.str命名空间只是 Python 侧的“语法糖”封装真正算子位于polars-plan的 DSL领域特定语言层再经由查询计划下发到执行引擎因此这些提取操作天然可以进入惰性查询、流式与分布式执行管线。五、混合 UTC 偏移与夏令时解析在 UTC转换到目标时区5.1 问题场景真实时序数据经常混有多种 UTC 偏移同一列里既有0100冬令时/标准时又有0200夏令时典型出现在跨夏令时切换边界的数据中。官方给出的示例数据正模拟了这一情形data [ 2021-03-27T00:00:000100, 2021-03-28T00:00:000100, # 切换日前夜 2021-03-29T00:00:000200, # 切换后已进入夏令时 2021-03-30T00:00:000200, ]5.2 Polars 的处理策略先归一为 UTC再统一迁移官方文档给出的核心策略有两条若数据包含混合 UTC 偏移的日期时间例如因夏令时切换导致Polars会以 UTC 解析它们。随后既可以向str.to_datetime传入目标time_zone也可以在解析后调用str.convert_time_zone。示例采用“解析后再转换”的写法mixed_parsed ( pl.Series(data) .str.to_datetime(%Y-%m-%dT%H:%M:%S%z) .dt.convert_time_zone(Europe/Brussels) ) print(mixed_parsed)格式串%Y-%m-%dT%H:%M:%S%z中%z匹配末尾形如0100的时区偏移由于各记录偏移不同Polars 先将时刻统一换算为 UTC 存储源码中str.to_datetime在输入带偏移时默认输出datetime[μs, UTC]可参见 string.py 中time_zone参数规则的第 2 条之后.dt.convert_time_zone(Europe/Brussels)把 UTC 时刻转换为布鲁塞尔时区的墙钟时间——此时每一条都带上了正确的CET/CEST语义。convert_time_zone的 Python 签名为convert_time_zone(self, time_zone: str)见 py-polars/src/polars/expr/datetime.py在 Rust DSL 层对应 crates/polars-plan/src/dsl/dt.rs 的convert_time_zone(time_zone)其中time_zone为 IANA 时区库名称如Europe/Brussels、Asia/Shanghai。5.3 两条路线的等价写法与边界“解析时指定目标时区”的等价写法为pl.Series(data).str.to_datetime( %Y-%m-%dT%H:%M:%S%z, time_zoneEurope/Brussels, )需要注意str.to_datetime的time_zone参数规则源码 string.py 有明确分档输入带偏移tz-aware且不传time_zone转换为UTC存储结果时区为UTC输入带偏移且传入time_zone先转 UTC、再转目标时区结果时区为目标时区输入不带偏移tz-naive且传time_zone是“贴标签”replaced而非“换算”converted即把本地墙钟时间直接标注为目标时区——这是一个容易踩坑的语义差异输入不带偏移且不传time_zone结果为无时区的datetime[μs]。5.4 处理“墙钟时间歧义”的ambiguous参数在时区回拨秋令时切换同一墙钟时间出现两次等场景还会产生歧义时刻。str.to_datetime为此提供了ambiguous参数见 string.py取值行为raise默认遇到歧义时刻直接报错earliest取最早的那个时刻latest取最晚的那个时刻null将歧义记录置为null这是对主文档“混合偏移”议题的自然延伸——偏移混合跨时区与歧义同一时区回拨是时区处理的一体两面。更系统的时区语义转换规则、DST 边界、tz-naive 与 tz-aware 的互操作请继续阅读本系列专题 timezones.md。六、解析完成之后进入完整时序工作流把数据正确解析为时间类型只是时序分析的入口。解析成功后即可无缝衔接本目录下的后续能力均与本文互为参照时间范围过滤用比较表达式按时间切片见 filter.md滚动窗口统计rolling系列算子见 rolling.md重采样与时间分组按固定频率聚合或向上/向下采样见 resampling.md更复杂的时区运算见 timezones.md。七、实战自检清单先看df.schema确认时间列是否如愿变成了Date/Datetime[μs]等类型警惕try_parse_dates推断失败后静默保留String。非 ISO8601 一律走显式转换用str.to_date/str.to_datetime配合格式串必要时先用strictFalse试转换、再用is_null()定位坏数据。混合偏移先归一后迁移默认结果就是 UTC需要本地墙钟时间时再用dt.convert_time_zone(Europe/Brussels)一步到位。明确 tz-naive 的“贴标签”语义对无时区数据传time_zone只是标注不是换算跨时区换算请先确保输入带偏移或先用dt.replace_time_zone明确语义。重活交给 schema能提前声明schema_overrides或用 Parquet 存储时就别依赖有开销的日期推断。【免费下载链接】polarsExtremely fast Query Engine for DataFrames, written in Rust项目地址: https://gitcode.com/GitHub_Trending/po/polars创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考