数据类型详解 Categorical Date Time 的区别与应用
在数据处理领域,类型系统常被视为一个“能用即可”的次要角色。许多开发者认为,只要程序能输出结果,选择何种数据类型似乎无关紧要。然而,事实真的如此吗?恰恰相反,正确理解并高效运用类型系统,往往是区分普通脚本与高性能、高健壮性应用程序的核心所在。它能带来的优势是立竿见影的:内存占用可能降低数十倍,计算性能提升数倍,代码逻辑也因此变得更加清晰可维护,潜在的缺陷自然大幅减少。
本文将深入探讨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等复杂类型的应用。熟练掌握这些知识,意味着您不仅能编写出可运行的代码,更能构建出高效、清晰、内存友好的高质量数据处理程序。数据处理的效率与优雅,往往就体现在对这些数据类型的精妙运用之中。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
OpenAI拟起诉苹果 ChatGPT集成效果未达预期引争议
5月16日,一则来自科技媒体Mashable的报道引发了行业关注:OpenAI正在评估对苹果公司采取法律行动的可能性。事件的导火索,是双方在2024年高调宣布的合作协议,其实际商业成果远未达到预期,OpenAI方面正考虑发出正式违约通知。 这场潜在的法律纠纷,核心在于双方对合作现状的严重分歧。时间拉
特斯拉将在拉斯维加斯建设大型Cybercab自动驾驶出租车专用洗车场
5月16日,行业媒体Teslarati披露,特斯拉在美国内华达州克拉克县的一项关键基础设施布局正加速落地。其核心是将一处占地约3 3万平方米的现有设施,全面升级为全球首个专为Cybercab无人驾驶出租车网络服务的高科技维护与清洁枢纽。 公开文件显示,特斯拉已于5月12日正式提交了名为“特斯拉中心C
奇瑞风云T9长续航版上市 售价11.99万起纯电续航220公里
2026年5月16日,奇瑞风云T9的长续航版本正式推向市场。官方给出的指导价区间在11 99万元到13 99万元之间,并且从即日起,限时享受红包优惠价,实际支付价格下探至10 99万至12 99万元。 这款新车在设计上延续了东方美学的思路。前脸部分采用了全新的“丝弦格栅”,设计灵感来源于中国传统古琴
宝马X1 M40中期改款2027年发布 新世代设计抢先看
去年冬季,宝马已悄然启动旗下燃油SUV的中期改款测试。本次改款的一大亮点在于,将逐步引入宝马全新的“新世代”家族设计语言。目前,作为先锋的高性能版本——X1 M40 xDrive,已进入实际道路测试阶段,预示着品牌设计更新策略的落地。 宝马此次设计更新策略明确,针对不同级别车型采取差异化方案。仅3系
理想L9正式交付常州杭州 全新旗舰车型开启用户交付
交付进程全面启动。5月17日,备受期待的全新理想L9率先于常州、杭州两地开启用户交付。此举意味着,这款旗舰级家庭SUV正式进入更多消费者的生活。自即日起,全国其他城市的交付工作也将按计划陆续展开,整体节奏持续提速。 回顾来看,这款被理想汽车定位为品牌首款“具身智能旗舰SUV”的新车型,于5月15日刚
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

