楼主: wayl
98 0

[其他] Polars:超越 Pandas,下一代高性能数据分析框架 [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

学前班

40%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
20 点
帖子
1
精华
0
在线时间
0 小时
注册时间
2018-8-28
最后登录
2018-8-28

楼主
wayl 发表于 2025-12-5 20:10:38 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

Polars:超越 Pandas,新一代高性能数据分析框架

在 Python 数据科学生态中,Pandas 长期占据主导地位。但随着数据规模不断增长以及多核处理器的广泛应用,Pandas 在性能和内存管理方面的局限性逐渐暴露。Polars 作为一个基于 Rust 语言从零构建的高效 DataFrame 库,凭借其卓越的速度和现代化的 API 设计,正在成为数据处理领域的新锐力量。本文将通过七个实际案例,全面展示 Polars 的核心优势与使用方式。

为何需要 Pandas 的替代方案?

  • 单线程执行限制:Pandas 多数操作依赖单线程,难以发挥现代 CPU 的多核并行能力。
  • 高内存消耗:尤其在处理字符串类型或进行某些转换时,Pandas 的内存占用较高。
  • API 设计不一致:由于历史原因,Pandas 的接口存在冗余与逻辑混乱的问题,增加学习和维护成本。
  • 计算效率低下:数据工程师常需长时间等待结果,严重影响开发迭代效率。

针对上述问题,Polars 应运而生。它以内存高效的 Apache Arrow 列式存储为基础,结合 Rust 语言的安全性与高性能,实现了自动并行化与深度查询优化。其设计目标包括:

  • 最大化并行处理能力:自动调度任务至所有可用 CPU 核心。
  • 智能查询优化:借助惰性求值机制,对整个操作流程进行分析并选择最优执行路径。
  • 声明式表达式 API:提供链式、可组合的操作接口,使代码更简洁、易读且稳定。
copy

准备工作与核心理念

开始使用 Polars 前,首先安装主包:

pip install polars

若需增强功能(如 CSV 解析或多库交互),可安装完整依赖:

pip install polars[all]

核心概念:表达式(Expressions)

Polars 的关键在于其“表达式”系统。不同于 Pandas 中直接执行的命令式操作,Polars 中的表达式并不立即运行,而是构建一个描述计算逻辑的对象。这些对象可以被组合、传递,并最终由底层引擎统一优化后并行执行。

df['A'] * 2

例如,在 Pandas 中调用 df['col'] 会立刻返回数据;而在 Polars 中,pl.col("col") 返回的是一个指向该列的表达式,用于后续参与复杂运算。这种延迟执行模式是实现高性能的关键之一。

pl.col('A') * 2

实战一:初识表达式 API

以下示例演示如何创建数据、选择字段及新增列,展现 Polars 表达式的灵活性。

import polars as pl

# 构造原始数据
data = {
    "city": ["New York", "London", "Tokyo", "Paris", "London"],
    "country": ["USA", "UK", "Japan", "France", "UK"],
    "temperature_c": [22.5, 15.0, 28.3, 25.1, 18.5],
    "humidity": [65, 72, 75, 68, 70],
}
df = pl.DataFrame(data)

# 1. 列选择
selected_df = df.select([
    pl.col("city"),
    pl.col("temperature_c")
])
print("--- Selected DataFrame ---\n", selected_df)

# 2. 添加新列
transformed_df = df.with_columns([
    (pl.col("temperature_c") * 9/5 + 32).alias("temperature_f"),
    pl.col("city").str.to_uppercase().alias("city_uppercase")
])
print("\n--- Transformed DataFrame ---\n", transformed_df)

结果说明

  • 不再通过字符串索引访问列,而是使用 pl.col() 创建列表达式。
  • 所有变换均封装在 select()with_columns() 方法内,便于整体优化。
  • .alias() 明确指定输出列名,提升语义清晰度。
df['col']

这种编程范式让 Polars 能够预先分析整个数据流图,合并冗余步骤、重排操作顺序,从而显著提升执行效率。

pl.col("col")

实战二:链式操作与高效过滤

Polars 支持流畅的链式调用风格,使得多步骤的数据清洗和转换过程结构清晰、易于理解。

import polars as pl

df = pl.DataFrame({
    "type": ["A", "B", "A", "C", "B", "A"],
    "value": [10, 20, 30, 40, 50, 60]
})

假设我们需要完成以下任务:

  • 筛选出 type 为 "A" 或 "B" 的记录
  • 将 value 加上一个偏移量
  • 按 type 分组并计算平均值
  • 按均值降序排列

使用 Polars 可以这样写:

result = (
    df
    .filter(pl.col("type").is_in(["A", "B"]))
    .with_columns((pl.col("value") + 5).alias("value_adj"))
    .group_by("type")
    .agg(pl.col("value_adj").mean())
    .sort("value_adj", descending=True)
)
print(result)

整个流程无需中间变量,逻辑连贯,且每个步骤都可能被查询优化器重新组织以获得最佳性能。

select

链式语法不仅提高了可读性,也增强了代码的可维护性,特别适合构建复杂的数据管道。

with_columns

此外,由于 Polars 默认启用多线程处理,像过滤、聚合这类操作会自动分布到多个核心上运行,进一步缩短执行时间。

.alias("new_name")

需求:筛选出类型为 'A' 的数据行,并将对应的 value 值扩大两倍。

在 Polars 中,可以通过以下方式实现:

result = (
    df.filter(pl.col("type") == "A")
     .select(
        (pl.col("value") * 2).alias("doubled_value")
     )
)
print(result)

结果分析:
通过

filter
select
的链式调用结构,整个数据处理流程被清晰地自上而下表达出来。这种声明式的编程风格相比 Pandas 中需要混合使用
[]
和多种方法调用的方式,在可读性和后期维护方面具有明显优势。

四、案例三:高效的分组聚合操作(
group_by

group_by
是数据分析中的核心步骤之一。Polars 的实现充分利用了多核并行计算能力,因此在执行效率上显著优于传统的 Pandas。

import polars as pl
import numpy as np

# 构造一个大规模数据集
num_rows = 1_000_000
data = {
    "category": np.random.choice(["A", "B", "C", "D"], num_rows),
    "value1": np.random.rand(num_rows) * 100,
    "value2": np.random.rand(num_rows) * 50,
}
df = pl.DataFrame(data)

# 需求说明:
# 按照 category 进行分组,并对每个组进行如下统计:
# 1. 统计行数(count)
# 2. 计算 value1 的总和(sum)
# 3. 计算 value2 的平均值(mean)
# 4. 计算 value1 的中位数(median)

aggregated_df = df.group_by("category").agg([
    pl.count().alias("num_rows"),
    pl.sum("value1").alias("sum_value1"),
    pl.mean("value2").alias("mean_value2"),
    pl.median("value1").alias("median_value1"),
    # 同时支持对同一列应用多个条件聚合
    pl.col("value1").filter(pl.col("value1") > 50).count().alias("count_value1_gt_50")
]).sort("category")

print(aggregated_df)

结果分析:

group_by().agg()
展现出强大的表达能力。你可以在
agg
中传入一组表达式,针对不同字段执行多样化的聚合逻辑,甚至嵌套过滤条件。所有这些操作都会被 Polars 查询引擎统一优化,并尽可能以并行方式执行,极大提升处理效率。

五、案例四:利用 Lazy API 实现查询优化

Lazy API 是 Polars 提供的一项高性能工具,常被视为其性能“核武器”。它允许用户先定义完整的数据转换流程,由系统生成并优化整体执行计划(如谓词下推、投影下推等),最后才真正触发计算。

import polars as pl

df = pl.DataFrame({
    "type": ["A", "B", "A", "C", "B", "A"],
    "value": [10, 20, 30, 40, 50, 60]
})

# 将普通 DataFrame 转换为惰性计算对象
lazy_df = df.lazy()

# 定义一系列操作(此阶段不执行任何实际运算)
query_plan = (
    lazy_df.filter(pl.col("value") > 25)
           .with_columns(
               (pl.col("value") * 10).alias("value_x10")
           )
           .select(["type", "value_x10"])
)

# 查看经过优化后的执行策略
print("--- Optimized Execution Plan ---")
print(query_plan.describe_optimized_plan())

# 触发实际计算并输出结果
print("\n--- Result ---")
result_df = query_plan.collect()
print(result_df)

结果分析:

.lazy()
将原始数据结构转为 LazyFrame,后续所有变换仅用于构建逻辑执行计划。通过调用
describe_optimized_plan()
,我们可以预览 Polars 对该查询的最终执行方案。通常会发现,系统自动将过滤操作
filter
前置执行,随后再进行数值计算
with_columns
,从而有效减少中间数据量——这正是“谓词下推”优化机制的体现。
最后通过
.collect()
启动真正的数据处理流程。对于大型数据集,尤其是从磁盘文件加载时,Lazy API 能够避免读取无关的行列,大幅节省内存占用并加快运行速度。

六、案例五:强大的窗口函数

窗口函数提供了一种在“窗口”范围内(即一组逻辑相关的行)执行计算的能力,而不会像传统聚合那样将多行合并为单行。这种机制保留了原始数据的结构,同时支持复杂的组内分析。

group_by
import polars as pl

df = pl.DataFrame({
    "department": ["Sales", "Sales", "Marketing", "Engineering", "Marketing"],
    "employee": ["Alice", "Bob", "Charlie", "David", "Eve"],
    "salary": [80000, 90000, 75000, 120000, 85000],
})

# 计算每位员工薪资与其所在部门平均薪资的差值
result_df = df.with_columns([
    (pl.col("salary") - pl.mean("salary").over("department")).alias("salary_diff_from_dept_avg")
])

print(result_df)
    

结果分析:

over
pl.mean("salary").over("department")

该操作的核心含义是:“按部门分组计算薪资的平均值,但不进行行折叠,而是将该平均值广播到对应组内的每一行”。这种方式极大简化了组内比较类的计算任务,代码简洁且执行效率高。相比 Pandas 中需借助 groupby().transform() 的实现方式,Polars 的语法更加直观易读。

department
salary
transform

七、案例六:高效的数据连接操作

数据连接(Join)是数据整合中的基础操作之一,Polars 提供了高性能且语义清晰的连接功能,适用于多种业务场景。

import polars as pl

# 用户表
df_users = pl.DataFrame({
    "user_id": [1, 2, 3],
    "name": ["Alice", "Bob", "Charlie"]
})

# 订单表
df_orders = pl.DataFrame({
    "order_id": [101, 102, 103],
    "user_id": [2, 3, 1],
    "amount": [150, 200, 100]
})

# 执行内连接并按用户ID排序
joined_df = df_users.join(df_orders, on="user_id", how="inner").sort("user_id")

print(joined_df)
    

结果分析:

join

Polars 的 join() 方法支持多种连接类型,包括内连接、左连接、外连接、半连接和反连接等。

inner
left
outer
semi
anti

其底层采用并行化的哈希连接算法,在处理大规模数据集时表现出优异的性能,远超传统单线程实现方式。

八、案例七:与 Pandas/NumPy 的无缝互操作

Polars 并非试图脱离现有的 Python 数据生态,而是通过 Apache Arrow 内存格式实现与 Pandas 和 NumPy 的高效数据交换,支持零拷贝或低成本转换,确保流程平滑过渡。

import polars as pl
import pandas as pd
import numpy as np

# Polars 转 Pandas
pl_df = pl.DataFrame({"a": [1, 2], "b": [3, 4]})
pd_df = pl_df.to_pandas()
print("--- Converted to Pandas ---\n", pd_df)
print(type(pd_df))

# Pandas 转 Polars
pl_df_from_pd = pl.from_pandas(pd_df)
print("\n--- Converted from Pandas ---\n", pl_df_from_pd)

# Polars 转 NumPy
numpy_array = pl_df.to_numpy()
print("\n--- Converted to NumPy ---\n", numpy_array)
print(type(numpy_array))
    

结果分析:

这一特性使得开发者可以在不同阶段灵活选用最适合的工具。例如,使用 Polars 完成大规模数据清洗与特征工程后,可将结果快速转为 NumPy 数组,直接输入机器学习模型进行训练,充分发挥各库的优势。

总结与展望

Polars 凭借基于 Rust 构建的高性能计算引擎、表达力强的表达式 API 以及惰性求值机制,显著提升了数据分析的效率与可维护性。它并不旨在取代 Pandas,而是在面对中大型数据集和高性能需求时,提供一个更优的替代方案。

当你仍在为 Pandas 的缓慢运行而困扰时,尝试 Polars 可能会带来耳目一新的体验。

当完成高效的数据处理与特征提取之后,下一步便是将这些高质量特征输入先进的 AI 模型,以解锁更深层次的智能应用。

从高性能数据处理迈向高级人工智能

对于想要探索数据分析成果与语言模型融合方式的开发者而言,

提供了一个理想的实验环境。在这里,你可以将 Polars 生成的分析结果作为上下文输入,免费且无限制地向多种主流模型如 Llama、Qwen、gpt-4o 等发起提问,还能体验每周轮换的 gpt-5 等前沿旗舰模型。

https://0v0.pro

Polars 专注于高效处理,实现“快”,而 AI 模型则承担理解与推理,体现“聪明”。两者的协同,标志着数据驱动应用迈入全新阶段。

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:pandas panda 数据分析 分析框架 Lars

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2025-12-21 21:57