Article
第1章:Polars 简介与环境搭建
1.1 什么是 Polars?与 Pandas 的对比
| 概念 | 说明 | 注意事项 |
|---|---|---|
| Polars 简介 | Polars 是一个基于 Apache Arrow 内存格式的高性能 DataFrame 库,使用 Rust 编写,支持多线程并行计算,提供 Python 和 Node.js 接口。其核心优势是速度快、内存效率高。 | Polars 专为现代 CPU 架构优化,适合处理大规模数据集(百万至十亿级行)。 |
| 与 Pandas 对比 | • 性能:Polars 使用向量化执行引擎和多线程,远快于 Pandas(尤其在聚合、过滤等操作)。 • 内存效率:基于 Arrow,零拷贝共享内存。 • API 设计:Polars 推崇表达式(Expression)编程,更函数式;Pandas 更命令式。 • 缺失值处理:Polars 明确区分 null 与 NaN,类型更安全。 • 惰性求值:Polars 支持 LazyFrame,可优化执行计划;Pandas 为立即执行。 | • 学习曲线略陡,需适应表达式风格。 • 某些 Pandas 小众功能尚未完全覆盖。 • 生态仍在发展中,第三方集成较少。 |
1.2 安装 Polars 与依赖环境配置
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| pip install polars | pip install polars | 安装核心 Polars 库 | pip install polars | 推荐使用虚拟环境(如 venv 或 conda)避免依赖冲突。 |
| pip install ‘polars[all]‘ | pip install 'polars[all]' | 安装带可选依赖的 Polars(如 parquet、xlsx) | pip install 'polars[parquet,xlsx]' | 使用 [all] 安装所有可选功能,适合数据分析环境。 |
| 验证安装 | import polars as pl | 检查是否成功导入 | import polars as plprint(pl.__version__) | 若报错,检查 Python 版本(需 3.8+)和 pip 是否为最新。 |
1.3 启用 Polars 的可选功能(如 lazy evaluation、parallel execution)
| 概念 | 说明 | 注意事项 |
|---|---|---|
| 惰性求值(Lazy Evaluation) | 使用 pl.LazyFrame 可延迟执行,构建执行计划后通过 collect() 触发。Polars 自动优化查询(如谓词下推、列投影)。 | 适合复杂管道,避免中间结果占用内存。 |
| 并行执行 | Polars 默认启用多线程并行(如 group_by、join),线程数由环境变量 POLARS_MAX_THREADS 控制。 | 无需手动配置,自动利用 CPU 多核。 |
| 启用 Arrow 扩展类型 | 可通过 pl.Config 启用对 Arrow 类型的兼容显示。 | 一般无需手动设置,内部已优化。 |
| 配置日志与调试 | 使用 pl.Config.set_fmt_str_lengths() 等调整输出格式。 | 用于调试长字符串截断等问题。 |
第2章:基础数据结构:Series 与 DataFrame
2.1 Series 的创建与基本操作
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
pl.Series() | pl.Series(name, values, dtype) | 创建 Series | s = pl.Series("age", [25, 30, 35]) | name 可省略,values 支持 list、numpy array 等。 |
.dtype | series.dtype | 查看数据类型 | s.dtype | 返回 Polars 类型(如 Int64、Utf8)。 |
.to_list() | series.to_list() | 转为 Python 列表 | s.to_list() | 大数据集慎用,可能占用内存。 |
.head() / .tail() | series.head(n) / series.tail(n) | 查看前/后 n 个元素 | s.head(2) | 默认 n=5。 |
.is_null() | series.is_null() | 返回布尔 Series,标记缺失值 | s.is_null() | 用于缺失值检测。 |
.fill_null() | series.fill_null(value) | 填充缺失值 | s.fill_null(0) | 支持多种策略(如 “forward”, “backward”)。 |
2.2 DataFrame 的创建方式(字典、列表、NumPy 数组等)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 字典创建 | pl.DataFrame({'col1': ..., 'col2': ...}) | 从字典创建 DataFrame | df = pl.DataFrame({ "name": ["Alice", "Bob"], "age": [25, 30]}) | 键为列名,值为列表或 Series。 |
| 列表创建 | pl.DataFrame([[val1, val2], [val3, val4]], schema=['a','b']) | 从嵌套列表创建 | pl.DataFrame([[1,2],[3,4]], schema=['x','y']) | 需指定 schema 定义列名。 |
| NumPy 数组 | pl.DataFrame(np.array(...), columns=...) | 从 NumPy 数组创建 | import numpy as npdata = np.array([[1,2],[3,4]])df = pl.DataFrame(data, columns=['a','b']) | 数组应为 2D。 |
| Series 列表 | pl.DataFrame([s1, s2]) | 从 Series 列表创建 | s1 = pl.Series("a", [1,2])s2 = pl.Series("b", [3,4])df = pl.DataFrame([s1, s2]) | Series 长度需一致。 |
| 空 DataFrame | pl.DataFrame() | 创建空 DataFrame | df = pl.DataFrame() | 可后续添加列。 |
2.3 查看与理解数据结构(shape, dtypes, head/tail, describe)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.shape | df.shape | 返回 (行数, 列数) | rows, cols = df.shape | 类似 NumPy/Pandas。 |
.dtypes | df.dtypes | 返回每列的数据类型列表 | df.dtypes | 顺序与列一致。 |
.columns | df.columns | 返回列名列表 | df.columns | 可用于列遍历。 |
.head(n) | df.head(n) | 查看前 n 行 | df.head(3) | 默认 n=5。 |
.tail(n) | df.tail(n) | 查看后 n 行 | df.tail(2) | 默认 n=5。 |
.describe() | df.describe() | 生成统计摘要(计数、均值、标准差等) | df.describe() | 仅对数值列有效。 |
.schema | df.schema | 返回字典:列名 → 数据类型 | df.schema | 比 .dtypes 更结构化。 |
第3章:数据读取与写入
3.1 读取 CSV 文件(read_csv)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
pl.read_csv() | pl.read_csv(source, ...) | 从 CSV 文件或路径读取数据 | df = pl.read_csv("data.csv") | source 可为文件路径、URL 或文件对象。 |
| separator | sep=',' | 指定分隔符 | pl.read_csv("data.tsv", sep='\t') | 默认逗号。 |
| has_header | has_header=True | 是否包含列名 | pl.read_csv("no_header.csv", has_header=False) | 若为 False,自动生成列名。 |
| columns | columns=[0,1] 或 ['a','b'] | 仅读取指定列 | pl.read_csv("big.csv", columns=["name","age"]) | 节省内存。 |
| dtypes | dtypes={'age': pl.Int32} | 指定列的数据类型 | pl.read_csv("data.csv", dtypes={"id": pl.Utf8}) | 避免类型推断错误。 |
| n_rows | n_rows=1000 | 仅读取前 n 行 | pl.read_csv("large.csv", n_rows=500) | 用于快速采样。 |
| use_pyarrow | use_pyarrow=True | 使用 PyArrow 后端(更快) | pl.read_csv("data.csv", use_pyarrow=True) | 需安装 pyarrow。 |
3.2 读取 Parquet 文件(read_parquet)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
pl.read_parquet() | pl.read_parquet(source, ...) | 读取 Parquet 格式文件 | df = pl.read_parquet("data.parquet") | Parquet 是列式存储,读取快且压缩率高。 |
| columns | columns=['a','b'] | 仅读取指定列 | pl.read_parquet("data.parquet", columns=["x"]) | 列投影优化,极快。 |
| filters | filters=[("age", ">", 30)] | 谓词下推,过滤行 | pl.read_parquet("data.parquet", filters=[("active", "==", True)]) | 减少内存使用。 |
| use_pyarrow | use_pyarrow=False | 是否使用 PyArrow 后端 | pl.read_parquet("data.parquet", use_pyarrow=True) | 默认使用 Polars 原生引擎。 |
3.3 读取 JSON、Excel、SQLite 等格式
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
pl.read_json() | pl.read_json(source) | 读取 JSON 文件(通常为 list of dicts) | df = pl.read_json("data.json") | 支持 line_delimited=True 读取 JSONL。 |
pl.read_excel() | pl.read_excel(source, sheet_name="Sheet1") | 读取 Excel 文件(.xlsx) | df = pl.read_excel("data.xlsx", sheet_name="Sales") | 需安装 openpyxl 或 xlsx2csv。 |
pl.read_database() | pl.read_database(query, connection) | 从数据库读取(如 SQLite) | import sqlite3conn = sqlite3.connect("db.db")df = pl.read_database("SELECT * FROM users", conn) | 支持任何 DB-API 2.0 连接。 |
pl.read_ndjson() | pl.read_ndjson(source) | 读取换行符分隔的 JSON | df = pl.read_ndjson("data.ndjson") | 适用于日志等流式数据。 |
3.4 写入数据到文件(write_csv, write_parquet 等)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.write_csv() | df.write_csv(file_path) | 写入 CSV 文件 | df.write_csv("output.csv") | 可指定 sep、header 等参数。 |
.write_parquet() | df.write_parquet(file_path) | 写入 Parquet 文件 | df.write_parquet("output.parquet") | 支持压缩(compression="zstd")。 |
.write_json() | df.write_json(file_path, orient="records") | 写入 JSON 文件 | df.write_json("output.json") | orient 可为 “records”、“lines” 等。 |
.write_excel() | df.write_excel(file_path) | 写入 Excel 文件 | df.write_excel("report.xlsx") | 需安装 xlsxwriter 或 openpyxl。 |
.write_database() | df.write_database(table_name, connection) | 写入数据库表 | df.write_database("users", conn) | 自动创建表(若不存在)。 |
.write_ndjson() | df.write_ndjson(file_path) | 写入换行符分隔的 JSON | df.write_ndjson("log.ndjson") | 适合流式写入。 |
第4章:数据查看与基本信息探索
4.1 查看前/后 N 行:head() 与 tail()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.head(n) | df.head(n) | 返回前 n 行 | df = pl.DataFrame({"a": [1,2,3,4,5], "b": ['x','y','z','w','v']})df.head(3) | 默认 n=5;若 n > row_count,返回全部数据。 |
.tail(n) | df.tail(n) | 返回后 n 行 | df.tail(2) | 默认 n=5;常用于查看末尾记录。 |
4.2 数据摘要:describe() 与 schema 信息
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.describe() | df.describe(percentiles=None) | 生成数值列的统计摘要 | df = pl.DataFrame({"age": [25,30,35,40], "score": [88,92,76,95]})df.describe() | 输出包括 count, mean, std, min, 25%, 50%, 75%, max;仅对数值类型列有效。 |
.schema | df.schema | 返回字典:列名 → Polars 数据类型 | df.schema | 类型为 pl.Int64, pl.Utf8 等,非字符串。 |
.glimpse() | df.glimpse() | 快速查看结构(类似 info()) | df.glimpse() | glimpse() 将列垂直显示,适合宽表。 |
4.3 列信息查看:columns, dtypes, shape
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.columns | df.columns | 返回列名列表 | cols = df.columns | 可用于遍历或重命名。 |
.dtypes | df.dtypes | 返回每列的数据类型列表 | types = df.dtypes | 顺序与 .columns 一致。 |
.shape | df.shape | 返回元组 (行数, 列数) | rows, cols = df.shape | 类似 NumPy 数组。 |
.height / .width | df.height / df.width | 分别获取行数和列数 | n_rows = df.height | 更语义化,避免索引混淆。 |
第5章:数据选择与索引
5.1 列选择:单列、多列、列切片
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 单列(属性) | df.column_name | 通过属性访问单列(Series) | df.age | 仅当列名是合法标识符时可用。 |
| 单列(键) | df["column"] | 通过键访问列 | df["age"] | 通用方式,支持任意列名(含空格)。 |
| 多列选择 | df[["col1", "col2"]] | 返回新 DataFrame 包含指定列 | df[["name", "age"]] | 列顺序可调整。 |
| 列切片 | df[start:end] | 按列位置切片(不推荐) | df[:2] | 实际是对行操作!列切片应使用 select()。 |
使用 select() | df.select("col1", "col2") | 推荐的列选择方式 | df.select(pl.col("name"), pl.col("age")) | 支持表达式,功能强大。 |
5.2 行选择:使用索引与布尔索引
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 布尔索引 | df[condition] | 根据条件筛选行 | df[df["age"] > 30] | 条件返回布尔 Series。 |
| 多条件(与) | (cond1) & (cond2) | 组合多个条件 | df[(df["age"] > 25) & (df["score"] < 90)] | 必须加括号,使用 & 而非 and。 |
| 多条件(或) | (cond1) | (cond2) | 任一条件满足 | df[(df["city"] == "Beijing") | (df["city"] == "Shanghai")] | 使用 | 而非 or。 |
| 索引选择行 | df[row_index] | 获取单行(返回 Series) | df[0] | 返回第 0 行作为 Series。 |
| 行范围 | df[start:end] | 获取行切片 | df[1:4] | 类似 Python 切片,左闭右开。 |
5.3 使用 select()、with_columns()、filter() 进行列/行筛选
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
select() | df.select(exprs) | 选择列或表达式结果 | df.select(pl.col("name").str.to_uppercase()) | 强大且高效,支持嵌套表达式。 |
with_columns() | df.with_columns(new_cols) | 添加或替换列 | df.with_columns((pl.col("age") + 1).alias("age_next")) | 不修改原 DataFrame,返回新实例。 |
filter() | df.filter(condition) | 筛选行(等价于布尔索引) | df.filter(pl.col("score") >= 80) | 更函数式风格,支持复杂表达式。 |
| 组合使用 | 链式调用 | 构建数据处理管道 | df.filter(pl.col("age") > 20) .select(["name", "score"]) .with_columns(pl.col("score").log().alias("log_score")) | 推荐写法,清晰且惰性优化友好。 |
5.4 使用 row() 和 item() 提取标量值
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.item() | expr.item() 或 series.item() | 从单元素 Series 或表达式提取标量 | single = df.filter(pl.col("name") == "Alice").select("age").item() | 必须确保结果只有一个元素,否则报错。 |
.row(i) | df.row(i) | 获取第 i 行为元组 | df.row(0) | 返回 Python 元组,不可变。 |
.rows() | df.rows() | 获取所有行为元组列表 | df.rows() | 大数据集慎用,可能占用内存。 |
.item(row, col) | df.item(row, col) | 通过行列索引提取单个值 | df.item(0, "age") | col 可为列名或索引。 |
第6章:数据清洗与预处理
6.1 处理缺失值:is_null(), fill_null(), drop_nulls()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.is_null() | expr.is_null() | 返回布尔值标记缺失 | df.select(pl.col("age").is_null()) | 用于检测 null 值。 |
.is_not_null() | expr.is_not_null() | 返回非缺失值标记 | df.filter(pl.col("email").is_not_null()) | 常用于过滤。 |
.fill_null() | expr.fill_null(value_or_strategy) | 填充缺失值 | df.with_columns(pl.col("age").fill_null(0)) | 支持 “forward”, “backward”, “mean” 等策略。 |
.drop_nulls() | df.drop_nulls(subset=None) | 删除含缺失值的行 | df.drop_nulls(subset=["age", "score"]) | subset 指定检查的列。 |
.drop_nans() | expr.drop_nans() | 删除 NaN(浮点特殊值) | pl.col("value").drop_nans() | 与 null 不同,需单独处理。 |
6.2 处理重复值:unique(), drop_duplicates()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.unique() | expr.unique() | 返回列的唯一值 | df.select(pl.col("city").unique()) | 保持首次出现顺序。 |
.n_unique() | expr.n_unique() | 返回唯一值数量 | df.select(pl.col("id").n_unique()) | 用于去重计数。 |
drop_duplicates() | df.drop_duplicates(subset=None) | 删除重复行 | df.drop_duplicates(subset=["email"]) | subset 指定基于哪些列判断重复。 |
| keep 参数 | keep="first" 或 keep="last" | 指定保留哪条重复记录 | df.drop_duplicates(keep="last") | 默认 “first”。 |
6.3 类型转换:cast(), strict_cast()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.cast() | expr.cast(dtype, strict=False) | 转换数据类型 | df.with_columns(pl.col("age_str").cast(pl.Int64)) | 若转换失败,默认转为 null。 |
.strict_cast() | expr.strict_cast(dtype) | 严格类型转换 | pl.col("num").strict_cast(pl.Float64) | 转换失败会抛出异常,适合验证数据质量。 |
| 常见类型 | pl.Int32 / pl.Float64 / pl.Utf8 / pl.Boolean / pl.Date 等 | Polars 数据类型 | pl.col("price").cast(pl.Float64) | 使用 Polars 类型枚举更安全。 |
6.4 字符串处理基础:str 模块常用方法
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.str.to_lowercase() | pl.col("col").str.to_lowercase() | 转小写 | df.select(pl.col("name").str.to_lowercase()) | |
.str.to_uppercase() | pl.col("col").str.to_uppercase() | 转大写 | df.with_columns(pl.col("tag").str.to_uppercase()) | |
.str.contains() | pl.col("col").str.contains("pattern") | 是否包含子串(支持正则) | df.filter(pl.col("email").str.contains("@gmail.com")) | 默认正则模式,设 literal=True 为字面匹配。 |
.str.replace() | str.replace("old", "new", literal=True) | 替换单个匹配 | pl.col("text").str.replace("old", "new") | 仅替换第一次出现。 |
.str.replace_all() | str.replace_all("old", "new") | 替换所有匹配 | pl.col("text").str.replace_all(" ", "_") | 推荐用于全局替换。 |
.str.strip() | str.strip() | 去除首尾空白 | pl.col("input").str.strip() | |
.str.split() | str.split("delim") | 按分隔符拆分为列表 | pl.col("tags").str.split(",") | 返回 List 类型列。 |
6.5 时间日期处理基础:dt 模块常用方法
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.str.strptime() | pl.col("col").str.strptime(pl.Date, "%Y-%m-%d") | 解析字符串为日期 | df.with_columns(pl.col("date_str").str.strptime(pl.Datetime, "%Y-%m-%d")) | 必须先解析才能使用 dt 模块。 |
.dt.year() | expr.dt.year() | 提取年份 | df.select(pl.col("dob").dt.year()) | |
.dt.month() | expr.dt.month() | 提取月份 | pl.col("ts").dt.month() | 返回整数 1-12。 |
.dt.day() | expr.dt.day() | 提取日 | pl.col("ts").dt.day() | |
.dt.hour() / .minute() / .second() | expr.dt.hour() | 提取时间部分 | pl.col("ts").dt.hour() | 适用于 Datetime 类型。 |
.dt.weekday() | expr.dt.weekday() | 返回星期几(1=Monday) | pl.col("ts").dt.weekday() | |
.dt.truncate() | expr.dt.truncate("1mo") | 截断到指定频率 | pl.col("ts").dt.truncate("1d") | 支持 “1h”, “1d”, “1w”, “1mo” 等。 |
第7章:数据操作:Filter、Sort、Sample
7.1 条件筛选:filter() 与布尔表达式
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
filter() | df.filter(condition) | 根据表达式筛选行 | df = pl.DataFrame({"name": ["A", "B", "C"], "age": [25, 30, 35]})df.filter(pl.col("age") > 28) | 推荐使用表达式风格,优于布尔索引。 |
| 比较运算符 | == / != / < / <= / > / >= | 构建基本条件 | pl.col("score") >= 80 | 返回布尔表达式。 |
| 逻辑与(and) | (cond1) & (cond2) | 多条件同时满足 | df.filter((pl.col("age") > 20) & (pl.col("score") < 90)) | 必须加括号,& 优先级高。 |
| 逻辑或(or) | (cond1) | (cond2) | 多条件任一满足 | df.filter((pl.col("city") == "Beijing") | (pl.col("city") == "Shanghai")) | 使用 | 而非 or。 |
| 逻辑非(not) | ~(condition) | 取反条件 | df.filter(~pl.col("active").is_null()) | ~ 表示否定。 |
is_in() | col.is_in([values]) | 判断是否在值列表中 | df.filter(pl.col("name").is_in(["Alice", "Bob"])) | 等价于 SQL 的 IN。 |
7.2 排序操作:sort()(单列与多列排序)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.sort() | df.sort(by, descending=False) | 按列排序 | df.sort("age") | 默认升序。 |
| 多列排序 | df.sort(["col1", "col2"], descending=[False, True]) | 先按第一列,再按第二列 | df.sort(["city", "age"], descending=[False, True]) | descending 可为布尔值或列表。 |
| 表达式排序 | df.sort(pl.col("name").str.lengths()) | 按表达式结果排序 | df.sort(pl.col("name").str.lengths()) | 支持复杂逻辑。 |
in_place | sort(..., in_place=False) | 是否原地修改 | 不推荐使用 in_place=True | Polars 多数操作返回新 DataFrame。 |
7.3 随机抽样:sample() 与分层抽样技巧
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.sample(n) | df.sample(n=5) | 随机抽取 n 行 | df.sample(n=3) | n 超过行数时返回全部。 |
.sample(frac) | df.sample(frac=0.5) | 按比例抽样 | df.sample(frac=0.2) | frac 为浮点数,0 < frac ≤ 1。 |
with_replacement | sample(..., with_replacement=False) | 是否放回抽样 | df.sample(n=2, with_replacement=True) | 放回可能重复抽取同一行。 |
shuffle | df.sample(frac=1.0) | 随机打乱行序 | shuffled = df.sample(frac=1.0) | 实现 shuffle 效果。 |
| 分层抽样 | 结合 group_by + map_groups | 按类别等比例抽样 | df.group_by("group") .map_groups(lambda g: g.sample(n=2)) | 注意性能,map_groups 较慢。 |
第8章:列操作与特征工程
8.1 添加新列:with_columns() 与 with_column()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
with_columns() | df.with_columns(new_exprs) | 添加或替换多列 | df.with_columns( (pl.col("price") * pl.col("qty")).alias("total"), pl.col("name").str.lengths().alias("name_len")) | 推荐使用,支持多个表达式。 |
with_column() | df.with_column(expr) | 添加单列(已弃用) | df.with_column(pl.col("age").log().alias("log_age")) | 建议统一使用 with_columns()。 |
| 列替换 | 同名列名 | 若列已存在则替换 | df.with_columns(pl.col("age").cast(pl.Int32)) | 不会报错,直接覆盖。 |
8.2 重命名列:rename() 与 alias()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.rename() | df.rename({"old": "new"}) | 重命名 DataFrame 的列 | df.rename({"age": "age_years", "score": "grade"}) | 接受字典映射。 |
.alias() | expr.alias("new_name") | 在表达式中重命名结果列 | df.select(pl.col("value").log().alias("log_value")) | 用于 select、with_columns 中。 |
| 批量重命名 | rename(dict) | 可一次重命名多个 | df.rename({"a": "col1", "b": "col2"}) | 未列出的列保持不变。 |
8.3 删除列:drop() 与 exclude()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.drop() | df.drop(["col1", "col2"]) | 删除指定列 | df.drop("temp_col") | 支持单列名或列表。 |
.exclude() | df.select(pl.exclude("col")) | 选择除指定列外的所有列 | df.select(pl.exclude("internal_id")) | 常用于排除敏感或临时列。 |
| 表达式排除 | pl.exclude(pl.Float64) | 排除特定类型列 | df.select(pl.exclude(pl.Boolean)) | 支持按类型过滤。 |
8.4 列的算术与逻辑运算
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 算术运算 | + / - / * / / / ** / % | 列间计算 | pl.col("x") + pl.col("y") | 自动对齐,支持广播。 |
| 逻辑运算 | & / | / ~ | 布尔逻辑 | (pl.col("a") > 0) & (pl.col("b") < 10) | 注意括号和运算符。 |
| 比较运算 | == / != / < 等 | 生成布尔列 | pl.col("status") == "active" | 返回 Boolean 类型。 |
| 数学函数 | pl.col().abs() / .sqrt() / .log() | 常用数学变换 | pl.col("value").log10() | 支持大多数 NumPy 风格函数。 |
8.5 使用 when().then().otherwise() 实现条件赋值
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
when().then() | pl.when(cond).then(val) | 条件为真时赋值 | df.with_columns( pl.when(pl.col("age") < 18) .then("minor") .otherwise("adult") .alias("category")) | 类似 SQL CASE WHEN。 |
.otherwise() | .otherwise(default_val) | 设置默认值(else 分支) | 必须以 .otherwise() 结尾 | 否则表达式不完整。 |
| 多重条件 | 链式 when().then() | 实现 elif 逻辑 | pl.when(pl.col("score") >= 90).then("A") .when(pl.col("score") >= 80).then("B") .otherwise("C") | 按顺序匹配,第一个为真即返回。 |
第9章:聚合与分组操作(GroupBy)
9.1 基本分组:group_by() 与聚合函数
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.group_by() | df.group_by("key") | 按列分组 | df = pl.DataFrame({"city": ["A","A","B"], "sales": [100,150,200]})df.group_by("city").agg(pl.col("sales").sum()) | 返回 GroupBy 对象,需接 agg。 |
.agg() | grouped.agg(expr) | 执行聚合 | df.group_by("city").agg(pl.sum("sales")) | 支持表达式。 |
| 常见聚合 | pl.sum() / pl.mean() / pl.count() / pl.min() / pl.max() | 基本统计 | pl.mean("score") | 可直接用列名或表达式。 |
9.2 多级分组与聚合
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 多列分组 | df.group_by(["col1", "col2"]) | 按多个列组合分组 | df.group_by(["dept", "year"]).agg(pl.sum("salary")) | 分组键为组合唯一值。 |
| 聚合多列 | agg([expr1, expr2]) | 一次计算多个聚合 | df.group_by("city").agg([ pl.sum("sales"), pl.mean("profit")]) | 提高性能,一次扫描。 |
9.3 使用 agg() 进行多种聚合统计
| 聚合函数 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
pl.sum() | pl.sum("col") | 求和 | pl.sum("revenue") | 忽略 null。 |
pl.mean() | pl.mean("col") | 均值 | pl.mean("rating") | |
pl.count() | pl.count() 或 pl.count("col") | 计数 | pl.count("id") | pl.count() 计所有行(含 null)。 |
pl.n_unique() | pl.n_unique("col") | 唯一值计数 | pl.n_unique("user_id") | |
pl.first() / pl.last() | pl.first("col") | 取每组首/尾值 | pl.last("timestamp") | 依赖行序。 |
pl.list() | pl.col("x").list() | 聚合为列表 | pl.col("item").list() | 保留组内所有值。 |
pl.std() / pl.var() | pl.std("col") | 标准差与方差 | pl.std("value") |
9.4 分组后过滤:filter() 与 having()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.filter() | df.group_by("key").filter(condition) | 先分组再过滤行(组内) | df.group_by("city").filter(pl.col("sales") > 100) | 过滤的是原始行,非聚合后。 |
.having() | grouped.agg(...).having(condition) | 过滤聚合后的组 | df.group_by("city") .agg(pl.sum("sales").alias("total")) .having(pl.col("total") > 200) | 类似 SQL 的 HAVING,作用于聚合结果。 |
9.5 分组内排序与窗口函数初步
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
sort_by()(agg) | pl.col("x").sort_by("y") | 分组内按某列排序 | df.group_by("group").agg(pl.col("value").sort_by("score")) | 用于获取排序后序列。 |
head() / tail() | pl.col("x").head(1) | 取每组前/后 N 个 | pl.col("ts").tail(1) | 常用于取最新记录。 |
rank() | pl.col("x").rank() | 分组内排名 | pl.col("score").rank(method="min") | 支持 “average”, “min”, “dense” 等。 |
cumsum() | pl.col("x").cumsum() | 累计求和(分组内) | pl.col("sales").cumsum() | 窗口函数基础。 |
over() | pl.col("x").sum().over("group") | 窗口聚合(不改变行数) | df.with_columns(pl.sum("val").over("category")) | 返回与原表同长,适合特征工程。 |
第10章:连接与合并数据(Joins)
10.1 内连接、外连接、左连接、右连接
| 连接类型 | 说明 | 注意事项 |
|---|---|---|
| 内连接(inner) | 只保留两表键值匹配的行 | 最常用,确保数据一致性。 |
| 左连接(left) | 保留左表所有行,右表无匹配则填充 null | 用于”主表 + 附加信息”场景。 |
| 右连接(right) | 保留右表所有行,左表无匹配则填充 null | 可通过交换左右表用左连接替代。 |
| 外连接(outer) | 保留两表所有行,无匹配处填充 null | 又称全连接(full join)。 |
| 半连接(semi) | 仅保留左表中在右表有匹配键的行 | 不合并数据,仅过滤。 |
| 反连接(anti) | 仅保留左表中在右表无匹配键的行 | 用于查找”缺失关联记录”。 |
10.2 使用 join() 方法进行 DataFrame 合并
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.join() | df1.join(df2, on="key", how="inner") | 按键合并两个 DataFrame | df1 = pl.DataFrame({"id": [1,2], "name": ["A","B"]})df2 = pl.DataFrame({"id": [1,3], "age": [25,30]})df1.join(df2, on="id", how="left") | on 指定连接键;how 指定连接类型。 |
how 参数 | how="inner" / "left" / "outer" / "semi" / "anti" | 控制连接行为 | df1.join(df2, on="id", how="outer") | 默认为 “inner”。 |
| 多键连接 | on=["key1", "key2"] | 使用多个列作为连接键 | df1.join(df2, on=["dept", "year"]) | 所有键必须同时匹配。 |
10.3 连接键的选择与重复列处理
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
on 参数 | join(..., on="col") | 指定同名列作为连接键 | df1.join(df2, on="user_id") | 两表必须都有该列。 |
left_on / right_on | join(left_on="col1", right_on="col2") | 指定不同名的连接键 | df1.join(df2, left_on="uid", right_on="user_id") | 用于列名不一致时。 |
suffix 参数 | suffix="_right" | 解决重复列名冲突 | df1.join(df2, on="id", suffix="_right") | 默认为 “_right”,避免覆盖。 |
| 预处理重命名 | df2.rename({"value": "value_y"}) | 手动控制列名 | 先重命名再连接 | 更灵活,推荐复杂场景使用。 |
10.4 使用 concatenate() 合并行(上下拼接)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
pl.concat() | pl.concat([df1, df2], how="vertical") | 垂直拼接多个 DataFrame | df1 = pl.DataFrame({"a": [1], "b": [2]})df2 = pl.DataFrame({"a": [3], "b": [4]})pl.concat([df1, df2]) | 默认 how="vertical",要求列名和类型兼容。 |
how="horizontal" | pl.concat([df1, df2], how="horizontal") | 水平拼接(按列合并) | pl.concat([df1, df3], how="horizontal") | 类似 pd.concat(..., axis=1),需行数相同。 |
how="diagonal" | pl.concat([df1, df2], how="diagonal") | 对角拼接,自动对齐列 | pl.concat([df1, df2], how="diagonal") | 适用于列部分重叠的表。 |
| 列不匹配处理 | require_same_dtype=False | 允许类型不同(会提升) | pl.concat([df1, df2], require_same_dtype=False) | 谨慎使用,可能导致精度损失。 |
第11章:宽长格式转换(Pivoting & Melting)
11.1 将宽表转为长表:melt()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.melt() | df.melt(id_vars, value_vars, variable_name, value_name) | 将多列转为键值对形式 | df = pl.DataFrame({"city": ["A","B"], "2023": [100,150], "2024": [110,160]})df.melt(id_vars="city", value_vars=["2023","2024"], variable_name="year") | 用于时间序列或指标列转换。 |
id_vars | id_vars="col" 或 ["col1"] | 保持不变的标识列 | id_vars="subject" | 通常为维度列。 |
value_vars | value_vars=["v1","v2"] | 要转换的值列 | 可省略,自动选择非 id_vars 列 | 建议显式指定。 |
variable_name / value_name | 自定义新列名 | 控制输出列名 | variable_name="year", value_name="sales" | 默认为 “variable” 和 “value”。 |
11.2 将长表转为宽表:pivot()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.pivot() | df.pivot(values, index, columns, aggregate_function) | 将长表重塑为宽表 | df = pl.DataFrame({"city": ["A","A","B"], "year": ["2023","2024","2023"], "sales": [100,110,150]})df.pivot(values="sales", index="city", columns="year") | 类似 Excel 数据透视表。 |
values | values="col" | 作为值填充的列 | values="score" | 必须为数值或可存储类型。 |
index | index="row_key" | 作为行索引的列 | index="student" | |
columns | columns="col_key" | 作为新列名的列 | columns="subject" | 唯一值将成为列名。 |
aggregate_function | aggregate_function="mean" | 处理重复单元格 | aggregate_function="sum" | 默认报错,可设 “first”, “mean” 等。 |
11.3 使用 pivot 进行交叉表统计
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 交叉表(crosstab) | df.pivot(values, index, columns, aggregate_function="count") | 统计类别组合频次 | df.pivot(values="index", index="gender", columns="status", aggregate_function="count") | values 可为任意列(常用索引)。 |
| 多值聚合 | 结合 with_columns | 添加总计行/列 | 先 pivot,再用 sum 计算 | Polars 不直接支持,需后处理。 |
| 布尔交叉表 | pl.lit(1) 作为值 | 构建存在性标记表 | df.with_columns(pl.lit(1).alias("present")).pivot(...) | 用于标记是否发生。 |
第12章:表达式(Expressions)编程范式
12.1 什么是表达式(Expression)?
| 概念 | 说明 | 注意事项 |
|---|---|---|
| 表达式(Expression) | 一个描述数据操作的惰性对象,如 pl.col("age") + 1 | 不立即执行,可组合、优化。 |
| 惰性求值 | 表达式在 collect() 或触发时才计算 | Lazy 模式下可优化执行计划。 |
| 函数式风格 | 表达式是纯函数,无副作用 | 易于测试和组合。 |
| 核心优势 | 支持谓词下推、列投影、自动并行 | 性能优于命令式循环。 |
12.2 常用表达式构建:col(), lit(), when().then() 等
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
pl.col() | pl.col("name") | 引用列 | pl.col("price") * 1.1 | 支持字符串、正则 r"^prefix"。 |
pl.lit() | pl.lit(100) | 创建标量值表达式 | pl.col("x") + pl.lit(5) | 将 Python 值嵌入表达式。 |
pl.when().then() | pl.when(cond).then(val) | 条件逻辑 | 见第 8.5 节 | 核心控制流工具。 |
pl.all() / pl.any() | pl.all() / pl.any(pl.Boolean) | 聚合布尔表达式 | pl.when(pl.all(pl.col("x") > 0)).then("positive") | pl.all() 检查所有行为真。 |
12.3 表达式的组合与嵌套
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 算术组合 | (expr1 + expr2) * expr3 | 数学运算 | (pl.col("a") + pl.col("b")) / 2 | 支持标准运算符。 |
| 逻辑组合 | (cond1) & (cond2) | 布尔逻辑 | (pl.col("age") > 18) & (pl.col("active")) | 必须加括号。 |
| 函数嵌套 | f(g(h(x))) | 多层变换 | pl.col("email").str.strip().str.to_lowercase() | 链式调用,从左到右执行。 |
| 列表表达式 | [expr1, expr2] | 批量操作 | df.select([pl.sum("x"), pl.mean("y")]) | 在 select, with_columns 中常用。 |
12.4 在 select、with_columns、filter 中使用表达式
| 上下文 | 用法 | 代码示例 | 注意事项 |
|---|---|---|---|
select() | 选择列或表达式结果 | df.select(pl.col("name").str.lengths()) | 只保留表达式结果列。 |
with_columns() | 添加/替换列 | df.with_columns(pl.col("age").log().alias("log_age")) | 保留原列,新增表达式列。 |
filter() | 筛选行 | df.filter(pl.col("score") >= pl.mean("score")) | 表达式返回布尔 Series。 |
group_by().agg() | 聚合计算 | df.group_by("cat").agg(pl.sum("val")) | 表达式在每组内计算。 |
sort() | 按表达式排序 | df.sort(pl.col("name").str.lengths()) | 支持复杂排序键。 |
第13章:惰性计算(Lazy Evaluation)
13.1 LazyFrame 简介与创建
| 概念 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| LazyFrame | pl.LazyFrame() 或 df.lazy() | 惰性 DataFrame,不立即执行 | df = pl.DataFrame({"a": [1,2,3], "b": [4,5,6]})lazy_df = df.lazy() | 所有操作返回 LazyFrame,延迟计算。 |
| 从文件创建 | pl.scan_csv("data.csv") | 惰性读取文件 | lazy_df = pl.scan_csv("sales.csv") | 不加载数据到内存,仅记录读取计划。 |
| 支持的扫描方法 | scan_csv() / scan_parquet() / scan_ipc() 等 | 惰性读取各种格式 | pl.scan_parquet("data.parquet") | 适用于大文件,支持谓词下推。 |
13.2 惰性操作链的构建
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 链式操作 | lazy_df.filter(...).group_by(...).agg(...) | 构建处理流水线 | result = (pl.scan_csv("log.csv") .filter(pl.col("status") == "success") .group_by("user_id") .agg(pl.sum("amount"))) | 每步返回 LazyFrame,无实际计算。 |
| 惰性兼容方法 | select() / with_columns() / sort() / join() 等 | 大多数操作支持惰性模式 | 同上 | 避免使用 .apply() 等强制求值操作。 |
| 谓词下推 | 自动优化 | 将过滤条件下推到读取阶段 | pl.scan_csv("data.csv").filter(pl.col("age") > 30) | 仅读取满足条件的块,节省 I/O。 |
| 列投影 | 自动优化 | 只读取后续需要的列 | pl.scan_csv("data.csv").select(["name", "age"]) | 减少内存占用。 |
13.3 执行计划查看:explain()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.explain() | lazy_df.explain() | 查看执行计划(文本) | print(lazy_df.explain()) | 显示优化后的逻辑计划和物理计划。 |
optimized 参数 | explain(optimized=True) | 控制是否显示优化后计划 | lazy_df.explain(optimized=False) | 调试时可对比优化前后差异。 |
| 执行成本提示 | 输出中包含 file_scan / filter / aggregate 等 | 识别性能瓶颈 | 关注扫描范围、聚合方式 | 帮助判断是否触发了谓词/列下推。 |
13.4 触发计算:collect() 与 collect_async()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.collect() | lazy_df.collect() | 触发计算,返回 DataFrame | df = lazy_df.collect() | 实际执行所有惰性操作。 |
| 错误处理 | 可能抛出 ComputeError | 数据问题在 collect 时暴露 | 用 try-except 包裹 | 建议在 collect 时验证数据质量。 |
collect_async() | await lazy_df.collect_async() | 异步收集(需 asyncio) | import asyncioasync def run(): df = await lazy_df.collect_async() return df | 提升 I/O 密集型任务的吞吐量。 |
| 性能建议 | 在链的末尾调用 | 避免多次 collect | 构建完整流水线后一次性 collect | 减少重复计算。 |
第14章:性能优化与最佳实践
14.1 避免 Python 循环:使用向量化操作
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 向量化表达式 | pl.col("x") + pl.col("y") | 替代 for 循环 | df.with_columns((pl.col("a") * pl.col("b")).alias("product")) | Polars 在 Rust 层优化执行。 |
避免 .apply() / .map() | 尽量不用 | 强制逐元素执行 Python 函数 | 使用 pl.when().then() 或数学表达式替代 | 性能极低,破坏惰性优化。 |
| 使用内置聚合 | pl.sum() / pl.mean() 等 | 高效统计 | df.group_by("key").agg(pl.sum("value")) | 比自定义函数快数十倍。 |
14.2 合理使用字符串与分类类型(Categorical)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
cast(pl.Categorical) | pl.col("col").cast(pl.Categorical) | 将低基数字符串转为分类类型 | df.with_columns(pl.col("city").cast(pl.Categorical)) | 节省内存,加速分组、连接。 |
| 字符串 vs 分类 | pl.Utf8 vs pl.Categorical | 存储与性能权衡 | 对 “gender”, “status” 等列使用分类 | 高基数(如姓名)不适合。 |
| 内存优势 | 分类类型存储整数索引 | 显著减少内存占用 | 百万行文本列可节省 90%+ 内存 | 尤其适合重复值多的列。 |
14.3 内存管理与数据类型优化
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 最小够用原则 | pl.Int8 / pl.Int16 / pl.Float32 等 | 使用最小必要精度 | df.with_columns(pl.col("age").cast(pl.Int8)) | 避免默认 Int64 浪费内存。 |
.memory_usage() | df.memory_usage() | 查看每列内存占用 | df.with_columns(pl.col("x").cast(pl.Float32)).memory_usage() | 诊断内存瓶颈。 |
| 及时释放变量 | del df / gc.collect() | 手动管理内存 | 处理大表后删除引用 | Python 垃圾回收不即时。 |
14.4 并行执行与多线程设置
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| Polars 自动并行 | 默认启用 | 多核执行聚合、连接等 | 无需额外代码 | 基于 Rayon 线程池,开箱即用。 |
| 设置线程数 | pl.Config.set_parallelism("rayon") 或环境变量 | 控制并发线程 | export POLARS_MAX_THREADS=4 | 避免过度占用资源。 |
| I/O 与计算分离 | collect_async() | 异步 I/O + 并行计算 | 结合异步框架使用 | 提升整体吞吐。 |
| GIL 绕过 | Rust 后端 | 不受 Python GIL 限制 | 所有表达式操作 | 真正的并行计算。 |
第15章:高级功能与扩展
15.1 自定义函数(UDF)与 map_elements()
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.map_elements() | pl.col("x").map_elements(func) | 对元素应用 Python 函数 | def square(x): return x ** 2df.with_columns(pl.col("a").map_elements(square)) | 破坏性能,仅在必要时使用。 |
| 类型注解 | returns=pl.Int64 | 帮助 Polars 推断返回类型 | .map_elements(f, returns=pl.Float64) | 避免类型错误。 |
| 替代方案 | 使用表达式组合 | 尽量避免 UDF | (pl.col("a") ** 2) | 表达式更快更安全。 |
15.2 窗口函数(Window Functions)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
.over() | expr.over("group") | 分组内窗口计算 | pl.col("score").rank().over("class") | 不改变行数,适合特征工程。 |
| 排名函数 | rank() / dense_rank() / row_number() | 分组内排序 | pl.col("val").rank(method="min").over("cat") | 支持多种排名策略。 |
| 累计函数 | cumsum() / cummax() / cumcount() | 累计统计 | pl.col("x").cumsum().over("group") | 按组内顺序累计。 |
| 移动窗口 | rolling() 结合 over | 滚动统计 | pl.col("price").rolling_mean(3).over("stock") | 需先排序。 |
15.3 结构体与嵌套数据处理
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| struct 类型 | pl.Struct | 存储嵌套字段 | pl.struct([pl.col("a"), pl.col("b")]).alias("pair") | 类似字典或 JSON 对象。 |
| 解构结构体 | .struct.field("name") | 提取子字段 | pl.col("pair").struct.field("a") | 支持链式访问。 |
| 处理列表列 | list 类型 + arr 模块 | 操作数组 | pl.col("items").list.lengths() | 适用于 JSON 数组字段。 |
15.4 与 NumPy、Pandas 的互操作
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 转 Pandas | .to_pandas() | 导出为 DataFrame | pdf = df.to_pandas() | 大数据集可能内存不足。 |
| 从 Pandas | pl.DataFrame(pdf) | 导入 Pandas 数据 | df = pl.DataFrame(pd_df) | 自动转换类型。 |
| 转 NumPy | .to_numpy() | 转为数组(单列) | arr = df["col"].to_numpy() | 多列需先 select 或 to_dict()。 |
| 共享内存 | 零拷贝转换(部分支持) | 减少内存复制 | df.to_pandas(use_pyarrow=True) | 推荐使用 PyArrow 作为桥梁。 |
15.5 使用 Polars 进行大数据处理(与 DuckDB 集成)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| DuckDB 读取 | pl.read_database() | 从 DuckDB 查询加载 | df = pl.read_database("SELECT * FROM tbl", connection) | 适用于超大表的预聚合。 |
| Polars 写入 DuckDB | df.write_database() | 将结果存入数据库 | df.write_database("table_name", connection) | 实现 ETL 流水线。 |
| 混合使用 | Polars 处理 + DuckDB 查询 | 各取所长 | 用 Polars 清洗,DuckDB 做复杂 SQL | DuckDB 支持 Parquet 直接查询。 |
| 性能优势 | 列式存储 + 向量化引擎 | 处理 GB~TB 级数据 | 结合 scan_parquet + collect() | 适合本地大数据分析场景。 |