游乐游手机版
首页/业界动态/文章详情

数据类型详解 Categorical Date Time 的区别与应用

时间:2026-05-18 14:09
合理运用Polars库的类型系统可提升数据处理效率与程序健壮性。其支持数值、字符串、日期时间及分类等类型,通过合理选择与转换能优化内存与计算性能。例如,对重复值多的字符串列使用分类类型可节省内存,日期时间与字符串模块提供便捷处理方法,灵活处理空值则保障数据质量。

在数据处理领域,类型系统常被视为一个“能用即可”的次要角色。许多开发者认为,只要程序能输出结果,选择何种数据类型似乎无关紧要。然而,事实真的如此吗?恰恰相反,正确理解并高效运用类型系统,往往是区分普通脚本与高性能、高健壮性应用程序的核心所在。它能带来的优势是立竿见影的:内存占用可能降低数十倍,计算性能提升数倍,代码逻辑也因此变得更加清晰可维护,潜在的缺陷自然大幅减少。

本文将深入探讨Polars这一高性能数据处理库的类型系统,揭示它如何成为您数据分析工作中的强大武器。

1. Polars支持哪些数据类型?

Polars提供了一套丰富且精细的数据类型体系,足以应对各类复杂的数据分析场景。您可以通过一个简单的命令来查看其支持的所有数据类型:

import polars as pl
# 查看所有数据类型
print(pl.datatypes)

从基础的整数、浮点数,到字符串、日期时间,再到更高级的分类、列表和结构体类型,Polars一应俱全。深入理解这些类型是高效使用该库的第一步。

2. 创建不同类型的列

在创建DataFrame时,指定列的数据类型非常简单直观。

整数和浮点数:Polars区分了不同精度的数值类型,例如Int8、Int16、Int32、Int64以及Float32、Float64。在处理海量数据时,选择合适的精度对内存优化至关重要。

df = pl.DataFrame({
    "int32_col": [1, 2, 3],
    "int64_col": [1, 2, 3],
    "float32_col": [1.5, 2.5, 3.5],
    "float64_col": [1.5, 2.5, 3.5],
})
# 查看数据类型
print(df.dtypes)
# [Int32, Int64, Float32, Float64]

字符串:字符串默认是Utf8类型。对于包含大量重复值的列,可以考虑使用Categorical(分类)类型,这能显著提升性能,下文将详细说明。

df = pl.DataFrame({
    "name": ["张三", "李四", "王五"],  # 默认 Utf8
    "code": pl.Series("code", ["A", "B", "C"], dtype=pl.Categorical)  # 分类
})

日期与时间:Polars对Python原生的日期时间类型提供了良好的支持,创建时通常能自动识别。

from datetime import date, datetime, time
df = pl.DataFrame({
    "pu re_date": [date(2024, 1, 1), date(2024, 1, 2)],
    "pu re_time": [time(10, 30), time(14, 45)],
    "pu re_datetime": [datetime(2024, 1, 1, 10, 30), datetime(2024, 1, 2, 14, 45)]
})
print(df)

输出结果清晰地展示了不同的日期时间类型:

┌───────────┬──────────┬─────────────────────┐
│ pu re_date ┆ pu re_time ┆ pu re_datetime       │
│ date      ┆ time      ┆ datetime[μs]       │
╞═══════════╪═══════════╪═════════════════════╡
│ 2024-01-01┆ 10:30:00  ┆ 2024-01-01 10:30:00│
│ 2024-01-02┆ 14:45:00  ┆ 2024-01-02 14:45:00│
└───────────┴───────────┴─────────────────────┘

3. 类型转换:cast方法

在数据清洗过程中,类型转换是常规操作。Polars使用cast()方法来完成这一任务。

df = pl.DataFrame({
    "num_str": ["1", "2", "3"],
    "price": [10.5, 20.3, 30.1]
})
# 字符串转整数
result = df.with_columns(
    pl.col("num_str").cast(pl.Int32).alias("num_int")
)
print(result)

转换后,新列的数据类型就变成了Int32:

┌─────────┬──────┬──────────┐
│ num_str ┆ price ┆ num_int  │
│ str     ┆ f64   ┆ i32      │
╞═════════╪══════╪══════════╡
│ 1       ┆ 10.5  ┆ 1        │
│ 2       ┆ 20.3  ┆ 2        │
│ 3       ┆ 30.1  ┆ 3        │
└─────────┴───────┴──────────┘

以下是一些常见的数据类型转换场景:

# 字符串 → 整数
pl.col("num_str").cast(pl.Int32)
# 整数 → 浮点数
pl.col("num_int").cast(pl.Float64)
# 浮点数 → 整数(直接截断)
pl.col("price").cast(pl.Int32)  # 10.5 → 10
# 四舍五入后转整数
pl.col("price").round(0).cast(pl.Int32)  # 10.5 → 11
# 日期 → 字符串
pl.col("date").cast(pl.Utf8)
# 字符串 → 日期
pl.col("date_str").str.to_date()
# 字符串 → 日期时间
pl.col("datetime_str").str.to_datetime()

4. Categorical:节省内存的利器

什么是Categorical类型?

简而言之,当某一列存在大量重复的字符串值时,使用Categorical(分类)类型可以带来惊人的内存节省和性能提升。其内部机制是使用整数编码来代表不同的类别,而非存储完整的原始字符串。

来看一个直观的对比示例:

# 生成100万行数据,城市只有‘北京’、‘上海’、‘深圳’3个类别
cities = ["北京", "上海", "深圳"] * 1000000

# 使用Utf8类型存储
df_utf8 = pl.DataFrame({"城市": cities})
print(f"Utf8内存占用: {df_utf8.get_column('城市').estimated_size() / 1024 / 1024:.2f} MB")

# 使用Categorical类型存储
df_cat = pl.DataFrame({"城市": pl.Series("城市", cities, dtype=pl.Categorical)})
print(f"Cat内存占用: {df_cat.get_column('城市').estimated_size() / 1024 / 1024:.2f} MB")

输出结果的对比非常悬殊:

Utf8内存占用: 57.00 MB
Cat内存占用: 0.50 MB

内存节省超过了99%!这对于处理大规模数据集具有重大意义。

创建Categorical列

主要有两种方式:

# 方法1:创建Series时直接指定
df = pl.DataFrame({
    "城市": pl.Series(["北京", "上海", "深圳"], dtype=pl.Categorical)
})

# 方法2:对现有列进行转换
df = pl.DataFrame({"城市": ["北京", "上海", "深圳"]})
df = df.with_columns(
    pl.col("城市").cast(pl.Categorical)
)

何时使用Categorical?

记住一个核心原则:低基数,高重复。典型场景包括性别、省份、产品类别、状态码等枚举值。对于像“用户ID”、“订单号”这种几乎每个值都唯一的高基数列,使用Categorical反而会增加额外的映射开销,得不偿失。

5. 日期时间处理:dt模块

Polars为日期时间列提供了强大的.dt访问器,让时间序列数据处理得心应手。

字符串转日期

df = pl.DataFrame({
    "date_str": ["2024-01-15", "2024-02-20", "2024-03-25"]
})
result = df.with_columns(
    pl.col("date_str").str.to_date().alias("date")
)
print(result)

提取年月日时分秒等组件

从日期时间中提取特定信息是常见需求:

df = pl.DataFrame({
    "dt": [datetime(2024, 1, 15, 10, 30, 45)]
})
result = df.with_columns(
    pl.col("dt").dt.year().alias("年"),
    pl.col("dt").dt.month().alias("月"),
    pl.col("dt").dt.day().alias("日"),
    pl.col("dt").dt.hour().alias("时"),
    pl.col("dt").dt.minute().alias("分"),
    pl.col("dt").dt.second().alias("秒"),
    pl.col("dt").dt.weekday().alias("星期几"),  # 1=周一, 7=周日
    pl.col("dt").dt.day_of_year().alias("一年第几天"),
)
print(result)

日期时间格式化

将日期时间转换为特定格式的字符串:

# 日期 → 字符串
result = df.with_columns(
    pl.col("dt").dt.strftime("%Y年%m月%d日").alias("中文格式"),
    pl.col("dt").dt.strftime("%Y-%m-%d").alias("ISO格式"),
    pl.col("dt").dt.strftime("%H:%M:%S").alias("时间格式"),
)
print(result)

日期计算与操作

进行日期的加减、截断等操作:

from datetime import timedelta
df = pl.DataFrame({
    "date": [date(2024, 1, 1), date(2024, 1, 15), date(2024, 2, 1)]
})
result = df.with_columns(
    # 加7天
    (pl.col("date") + timedelta(days=7)).alias("加7天"),
    # 减3天
    (pl.col("date") - timedelta(days=3)).alias("减3天"),
    # 截断到月初
    pl.col("date").dt.truncate("1mo").alias("月初"),
    # 获取月末日期
    pl.col("date").dt.month_end().alias("月末"),
    # 获取所属季度
    pl.col("date").dt.quarter().alias("季度"),
)
print(result)

6. 字符串处理:str模块

字符串操作通过.str访问器进行,功能全面且强大。

常用字符串操作

df = pl.DataFrame({
    "name": ["Zhang San", "LI SI", "Wang Wu"],
    "email": ["zhang@qq.com", "li@163.com", "wang@gmail.com"]
})
result = df.with_columns(
    # 转换为大写
    pl.col("name").str.to_uppercase().alias("大写"),
    # 转换为小写
    pl.col("name").str.to_lowercase().alias("小写"),
    # 首字母大写(标题格式)
    pl.col("name").str.to_titlecase().alias("首大写"),
    # 计算字符串长度
    pl.col("name").str.lengths().alias("长度"),
    # 提取@符号前的邮箱前缀
    pl.col("email").str.strip_prefix("@").alias("邮箱前缀"),
)
print(result)

字符串包含与替换

result = df.with_columns(
    # 判断是否包含特定子串
    pl.col("email").str.contains("qq").alias("是QQ邮箱"),
    # 判断是否以某字符串开头
    pl.col("email").str.starts_with("zhang").alias("是zhang开头"),
    # 替换子串
    pl.col("email").str.replace("gmail", "outlook").alias("替换后"),
    # 移除字符串前后的空白字符
    pl.col("name").str.strip().alias("去空格"),
)
print(result)

使用正则表达式提取子串

df = pl.DataFrame({
    “text”: [“订单号: A12345”, “订单号: B67890”, “订单号: C11111”]
})
result = df.with_columns(
    # 使用正则表达式提取数字部分
    pl.col(“text”).str.extract(r”(\d+)”, 0).alias(“订单号”),
)
print(result)

7. Null值处理

现实世界的数据中,Null(空值)无处不在。Polars提供了灵活多样的处理方式。

检测Null值

df = pl.DataFrame({
    "name": ["张三", None, "王五"],
    "age": [25, 30, None],
    "salary": [8000, None, 12000]
})
# 检查是否为Null
result = df.select(
    pl.col("name").is_null().alias("name是Null"),
    pl.col("age").is_not_null().alias("age非Null"),
)
print(result)

填充Null值

# 用固定值填充
result = df.with_columns(
    pl.col("age").fill_null(0).alias("age填0"),
    pl.col("salary").fill_null(pl.col("salary").mean()).alias("salary填均值"),
)
# 用前向或后向值填充
result = df.with_columns(
    pl.col("name").fill_null(strategy="forward").alias("用前值填充"),
)

删除包含Null值的行

# 删除任何列包含Null的行
result = df.drop_nulls()
# 仅删除指定列包含Null的行
result = df.drop_nulls(subset=["salary"])

8. List和Struct类型

List类型:存储值序列

List类型允许在一列中存储数组或列表,非常适合存储如多次考试成绩、用户浏览历史、标签列表等序列化数据。

df = pl.DataFrame({
    "name": ["张三", "李四"],
    "scores": [[90, 85, 92], [78, 88, 95]]
})
result = df.with_columns(
    # 计算List长度
    pl.col("scores").list.lengths().alias("考试次数"),
    # 求List中的最大值
    pl.col("scores").list.max().alias("最高分"),
    # 求List中的平均值
    pl.col("scores").list.mean().alias("平均分"),
)
print(result)

Struct类型:组合字段

Struct类型可以将多个相关的字段组合成一个列,类似于字典、JSON对象或命名元组,便于管理复杂嵌套数据。

df = pl.DataFrame({
    "name": ["张三", "李四"],
    "info": [
        {"age": 25, "city": "北京"},
        {"age": 30, "city": "上海"}
    ]
})
result = df.with_columns(
    pl.col("info").struct.field("age").alias("年龄"),
    pl.col("info").struct.field("city").alias("城市"),
)
print(result)

9. 实战:完整数据清洗流程

将上述知识点串联起来,完成一个完整的数据清洗流程示例:

raw_df = pl.DataFrame({
    "order_id": ["A-001", "B-002", "C-003", None],
    "customer": ["Zhang", "LI", "Wang", "Zhao"],
    "amount": ["100", "200", "abc", "400"],
    "date": ["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04"],
    "category": ["电子产品", "电子产品", "服装", "服装"]
})

# 完整清洗流程
clean_df = (
    raw_df
    # 1. 删除order_id为Null的行
    .drop_nulls()
    # 2. 类型转换与清洗
    .with_columns(
        # 尝试转换金额,无效值会变为Null
        pl.col("amount").cast(pl.Int32, strict=False).alias("金额"),
        pl.col("date").str.to_date().alias("日期"),
    )
    # 3. 删除转换后金额为Null的行(如‘abc’)
    .drop_nulls(subset=["金额"])
    # 4. 分类列使用Categorical节省内存
    .with_columns(
        pl.col("category").cast(pl.Categorical)
    )
    # 5. 新增计算列(例如计算含税金额)
    .with_columns(
        (pl.col("金额") * 1.1).alias("含税金额"),
    )
    # 6. 选择并重排最终需要的列
    .select(["order_id", "customer", "金额", "含税金额", "日期", "category"])
)
print(clean_df)
print(clean_df.dtypes)

10. 避坑指南

在实际使用中,有几个常见的“坑”需要注意避开。

坑1:字符串转日期时格式不匹配

# ❌ 错误:默认格式可能不匹配,导致解析失败或错误
pl.col("date_str").str.to_date()  # 未指定格式
# ✅ 正确:明确指定日期字符串的格式
pl.col("date_str").str.to_date("%Y/%m/%d")

坑2:误将高基数列转换为Categorical

# ❌ 错误:像‘姓名’这种几乎每个值都不同的高基数列,转换会降低性能
pl.col("姓名").cast(pl.Categorical)  # 内存开销反而更大
# ✅ 正确:仅对低基数、高重复的列使用Categorical
pl.col("城市").cast(pl.Categorical)

坑3:浮点数直接转换为整数导致精度丢失

# ❌ 错误:price=10.9会直接截断为10,丢失小数部分
pl.col("price").cast(pl.Int32)
# ✅ 正确:应先进行四舍五入,再转换
pl.col("price").round(0).cast(pl.Int32)

11. 总结

本文系统性地梳理了Polars库强大的类型系统。从基础数据类型的创建与转换,到能极大优化内存的Categorical类型,再到功能强大的日期时间(.dt)和字符串(.str)处理模块,最后涵盖了Null值处理以及List、Struct等复杂类型的应用。熟练掌握这些知识,意味着您不仅能编写出可运行的代码,更能构建出高效、清晰、内存友好的高质量数据处理程序。数据处理的效率与优雅,往往就体现在对这些数据类型的精妙运用之中。

来源:https://www.51cto.com/article/843417.html
上一篇数据库性能优化十大技巧从索引失效到查询提速 下一篇16MB轻量插件SideX让VS Code运行更流畅
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
长安汽车明年一季度发布首款车载人形机器人小安
业界动态 · 2026-06-29

长安汽车明年一季度发布首款车载人形机器人小安

长安汽车公布机器人战略,采用“1+N+X”布局,联合头部伙伴攻克大脑、能源、驱动技术。人形机器人“小安”身高169cm,体重69kg,移动速度0 8m s,具备40个自由度,续航超2小时。预计明年一季度发布首款车载组件机器人,已在广州车展展示。

中国信科刷新光通信世界纪录 每秒可下载1.4万部4K电影
业界动态 · 2026-06-29

中国信科刷新光通信世界纪录 每秒可下载1.4万部4K电影

3月25日,光通信领域迎来又一个里程碑:中国信科集团光通信技术和网络全国重点实验室联合鹏城实验室、烽火藤仓光纤科技有限公司,成功实现了2 5Pb s 24芯光纤超大容量实时光传输,再次刷新了世界纪录。 这一研究成果不仅入选国际顶级光通信会议OFC(2026)并荣获“高分论文”称号,还受国际权威SCI

美国调查18万辆特斯拉Model3车门应急释放装置易找性
业界动态 · 2026-06-29

美国调查18万辆特斯拉Model3车门应急释放装置易找性

美国国家公路交通安全管理局对约17 9万辆2024款特斯拉Model3启动缺陷调查,焦点在于车门应急释放装置是否不易找到且标识不清。该调查源于一份缺陷请愿,不意味着立即召回,但可能引发后续监管措施。

doc个人图书馆停服 创始人称无偿转让失败
业界动态 · 2026-06-29

doc个人图书馆停服 创始人称无偿转让失败

运营长达20年,累计服务8000万用户的360doc个人图书馆,最终还是迎来了谢幕时刻。2026年5月1日,这个承载着无数用户收藏记忆的知名平台将正式停止服务——关停原因并非用户流失,而是始终未能寻得一位能够安全接管的合适人选。 创始人蔡智在告别信中坦言,近两个月来,他一直在尝试将360doc无偿转

年Q1随身WiFi实测安全靠谱高性价比机型推荐
业界动态 · 2026-06-29

年Q1随身WiFi实测安全靠谱高性价比机型推荐

2025年10月,艾瑞咨询正式授予飞猫“AI WiFi品类开创者”认证,紧接着CIC也将其认定为“多网融合自由切换技术服务首创者”。这些权威认证背后,折射出一个清晰的市场趋势:移动办公、户外出行、宿舍上网等场景的需求正在快速增长,随身WiFi几乎已成为不少用户的刚需装备。但问题也随之而来——网络卡顿