2.1 Series:一维数组
Series 是一列类型统一的具名数据。你很少单独创建它,但理解它有助于理解 DataFrame。
import polars as pl
s = pl.Series("age", [30, 25, 35])
print(s.dtype) # Int64
print(s.sum()) # 90
print(s.mean()) # 30.0
print(s.name) # "age"
2.2 数据类型(dtype)
Polars 的类型系统直接对应 Arrow 类型,比 pandas 更精确、更丰富。
- Int8 / Int16 / Int32 / Int64
- 有符号整数,位宽不同。默认整数字面量推断为
Int64。选小位宽可省内存。 - UInt8 / UInt16 / UInt32 / UInt64
- 无符号整数,用于计数、索引等非负场景。
- Float32 / Float64
- 浮点数。默认浮点字面量推断为
Float64。缺失值用null表示,而非 pandas 的NaN(Polars 里NaN与null是两回事)。 - Utf8(String)
- 字符串类型,底层是 Arrow 变长 UTF-8。比 pandas 的
object数组高效得多,是 Polars 处理文本快的关键。 - Boolean
- 布尔类型,用位图存储,过滤/掩码运算极快。
- Categorical
- 类别类型:把重复字符串编码为整数 + 字典。适合低基数(取值种类少)的列,如"城市""性别",省内存且分组更快。
- Date / Datetime / Duration / Time
- 时间相关类型。
Datetime可带时区与精度(ms/us/ns),Duration表示时间差。详见第 8 章。 - List / Struct
- 嵌套类型:
List是变长同类型数组(一个单元格里装多个值),Struct是命名字段的组合(类似一行 JSON)。详见第 9 章。
2.3 创建 DataFrame
# 方式 1:从字典(列 → 值列表)
df = pl.DataFrame({
"id": [1, 2, 3],
"price": [9.9, 19.9, 29.9],
})
# 方式 2:显式指定 schema(列名 + dtype)
df = pl.DataFrame(
{"id": [1, 2], "tag": ["a", "b"]},
schema={"id": pl.UInt32, "tag": pl.Categorical},
)
# 方式 3:从行(list of dict)
df = pl.DataFrame([
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"},
])
2.4 读取文件
# CSV
df = pl.read_csv("data.csv")
# Parquet(列式格式,强烈推荐用于中间存储)
df = pl.read_parquet("data.parquet")
# JSON / NDJSON
df = pl.read_json("data.json")
df = pl.read_ndjson("data.ndjson")
# 带参数:指定分隔符、类型、只读部分列
df = pl.read_csv(
"data.csv",
separator=";",
columns=["id", "price"], # 只读这两列
schema_overrides={"id": pl.UInt32},
)
read_ vs scan_
每个 read_xxx 都有个惰性版 scan_xxx(如 scan_csv、scan_parquet)。scan_ 返回 LazyFrame,不立即读入内存,配合优化器只读需要的列和行——处理大文件时永远优先用 scan_。第 7 章详解。
2.5 检查 DataFrame
print(df.shape) # (行数, 列数),如 (1000, 5)
print(df.columns) # ['id', 'price', ...]
print(df.dtypes) # [Int64, Float64, ...]
print(df.schema) # {'id': Int64, 'price': Float64}
df.head(5) # 前 5 行
df.describe() # 各列统计摘要(count/mean/std/min/max...)
df.estimated_size("mb") # 估算内存占用
schema 是什么
schema 是"列名 → dtype"的有序映射,是 DataFrame 的类型契约。在 Lazy 模式下,Polars 在真正执行前就能通过 schema 推断出每一步的结果类型,从而提前发现类型错误并做优化。
2.6 null 缺失值
Polars 用统一的 null 位图(validity bitmap)表示缺失,任何类型都能有 null,无需像 pandas 那样把整型列升级成浮点来塞 NaN。
df = pl.DataFrame({"x": [1, None, 3]})
df.null_count() # 每列的 null 数量
df.select(pl.col("x").is_null()) # 布尔列
df.drop_nulls() # 删除含 null 的行
df.fill_null(0) # 用 0 填充
df.fill_null(strategy="forward") # 前向填充
null ≠ NaN
在 Polars 里 null(缺失)和 NaN(非数,浮点特殊值)是两个不同概念。is_null() 只匹配缺失,is_nan() 只匹配 NaN。这与 pandas 混用 NaN 表示缺失的做法不同,迁移时务必注意。
2.7 写出文件
df.write_csv("out.csv")
df.write_parquet("out.parquet", compression="zstd")
df.write_ndjson("out.ndjson")
2.8 小结与下一步
- Series 是一列、DataFrame 是表;类型系统对应 Arrow,比 pandas 精确丰富。
- 核心类型:整型/浮点/Utf8/Boolean/Categorical/时间/List/Struct。
- 创建可从字典/行/文件;大文件优先用惰性的
scan_*。 - 缺失值用统一 null 位图,注意
null ≠ NaN。 - 下一章进入 Polars 的灵魂:表达式 Expression API。