详解 DataFrame 高性能处理工具 – Polars

Polars 是一个现代化的、高性能的 DataFrame 库,旨在为大规模数据处理提供极快的性能。它是用 Rust 编写的,并且专注于并行处理和内存效率。相比于 pandasPolars 在处理大规模数据时可以更高效,尤其在处理多核处理器和大内存的环境下,性能优势更加明显。

1. Polars 简介

  • 目标:Polars 旨在为数据科学家、分析师、工程师等提供一个既高效又易于使用的数据处理库,特别是对于需要处理大量数据的应用场景。
  • 语言支持:Polars 支持 Python、Rust、Node.js 等多种语言。
  • 架构:基于 Rust 的多线程并行计算,使用 Arrow 库作为数据存储格式,从而提高内存效率和计算性能。
  • 特点
    • 高性能:Polars 是用 Rust 编写的,Rust 是一门以性能为重点的系统编程语言,Polars 的核心依靠 Rust 的零成本抽象、并行处理和内存管理优势。
    • 易用性:API 设计灵感来源于 pandas,容易上手,适合已有 pandas 使用经验的用户。
    • 延迟计算:Polars 支持延迟计算,计算图不会立即执行,直到你调用 .collect()
    • 多线程并行处理:在多核 CPU 上,Polars 能够有效地并行处理数据,利用多核加速计算。

2. 安装 Polars

2.1 安装 Python 版本

Polars 在 Python 中提供了丰富的功能,可以通过 pip 安装:

pip install polars

2.2 安装 Rust 版本

Rust 版本的 Polars 可以通过 Cargo 进行安装:

cargo install polars

3. Polars 与 Pandas 对比

Polars 的设计是为了在高性能、大规模数据处理上超越 pandas,特别是对于那些处理数百万、数十亿条数据的任务。

特性PandasPolars
语言Python(内核用 Cython)Rust(绑定到 Python、Node.js 等)
数据存储DataFrame 和 Series(基于 NumPy)Arrow 数据格式(列式存储)
并行处理单线程多线程并行处理,自动分配工作到多个核心
延迟计算支持延迟计算,只有调用 .collect() 时才计算
内存效率适中内存更高效,减少内存复制和数据传输
操作速度慢于 Polars更快,尤其在大数据集上,速度优势明显

4. Polars 核心概念

4.1 DataFrame 和 Series

  • DataFrame:类似于 pandas 中的 DataFrame,是一个二维的表格结构,包含行和列。
  • Series:类似于 pandas 中的 Series,是单一列的数据结构。

4.2 延迟计算(LazyFrame)

Polars 的延迟计算可以通过 LazyFrame 来实现。延迟计算可以将多个操作合并成一个优化过的查询,这样可以避免中间结果的计算,提高整体性能。只有在调用 .collect() 时,计算才会真正执行。

import polars as pl

# 延迟计算
lazy_df = pl.scan_csv('data.csv')  # 延迟加载数据
result = lazy_df.filter(pl.col('age') > 30).select([pl.col('name'), pl.col('age')])

# 执行并收集结果
df = result.collect()
print(df)

4.3 多线程并行处理

Polars 利用多线程来加速计算,特别是对于 CPU 密集型的操作。Polars 会自动在多个 CPU 核心上并行执行任务,大幅提高数据处理的速度。

5. Polars 的基本用法

5.1 创建 DataFrame

你可以使用字典、列表、Numpy 数组等方式创建 DataFrame。

import polars as pl

# 从字典创建 DataFrame
df = pl.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie'],
    'age': [25, 30, 35],
    'city': ['New York', 'Los Angeles', 'Chicago']
})

print(df)

输出

shape: (3, 3)
┌─────────┬─────┬────────────┐
│ name    │ age │ city       │
│ ---     │ --- │ ---        │
│ str     │ i64 │ str        │
├─────────┼─────┼────────────┤
│ Alice   │ 25  │ New York   │
│ Bob     │ 30  │ Los Angeles│
│ Charlie │ 35  │ Chicago    │
└─────────┴─────┴────────────┘

5.2 数据选择和过滤

与 pandas 相似,Polars 支持通过列名选择和过滤数据。

# 选择单列
print(df['name'])

# 多列选择
df_filtered = df.select(['name', 'city'])
print(df_filtered)

# 过滤数据
df_age_filtered = df.filter(pl.col('age') > 30)
print(df_age_filtered)

5.3 基本聚合操作

Polars 支持基本的聚合操作,如 groupbysummeancount 等。

# 按 city 分组并计算 age 的平均值
df_grouped = df.groupby('city').agg([pl.col('age').mean().alias('avg_age')])
print(df_grouped)

5.4 使用延迟计算(LazyFrame)

延迟计算对于大数据集的处理非常有用。通过 scan_* 方法,Polars 提供了延迟计算的功能。

lazy_df = pl.scan_csv('data.csv')
result = lazy_df.filter(pl.col('age') > 30).select(['name', 'age'])
df_result = result.collect()  # 执行并收集结果
print(df_result)

6. Polars 高级功能

6.1 数据连接(Join)

Polars 提供了多种连接方式(如内连接、外连接等),用于将不同的 DataFrame 合并。

df1 = pl.DataFrame({
    'id': [1, 2, 3],
    'name': ['Alice', 'Bob', 'Charlie']
})

df2 = pl.DataFrame({
    'id': [1, 2, 4],
    'age': [25, 30, 35]
})

# 内连接
df_joined = df1.join(df2, on='id', how='inner')
print(df_joined)

6.2 支持多种文件格式

Polars 支持多种文件格式的读取和写入,包括 CSV、JSON、Parquet 等。

# 读取 CSV
df = pl.read_csv('data.csv')

# 写入 Parquet
df.write_parquet('output.parquet')

6.3 数据类型转换

Polars 支持多种数据类型转换功能,例如将字符串列转换为日期时间类型:

df = pl.DataFrame({
    'date': ['2021-01-01', '2022-02-02', '2023-03-03']
})

df = df.with_columns(pl.col('date').str.strptime(pl.Date, fmt='%Y-%m-%d'))
print(df)

7. Polars 性能优势

7.1 性能优化

Polars 依赖 Rust 和 Arrow 技术栈,这使得它在性能上远超 Python 的 pandas,尤其在大规模数据集上,Polars 的性能表现十分出色。Polars 内部使用列式存储格式(Arrow),能够高效地处理列操作。

7.2 多核并行

Polars 在多核机器上表现尤为突出,能够自动并行化数据处理任务,充分利用现代 CPU 的多核优势。

7.3 低内存消耗

Polars 使用内存池(memory pool)和零复制技术,使得它的内存消耗非常低。尤其是在需要处理大数据集时,Polars 可以显著减少内存占用。

8. 总结

Polars 是一个功能强大、高性能的数据处理库,特别适合于处理大规模数据集。与 pandas 相比,Polars 在性能和内存效率方面具有显著的优势,尤其在大数据和并行计算场景下表现出色。它提供了类似

pandas 的 API,易于上手,并支持延迟计算、多线程和多种文件格式的读写。如果你处理的数据量非常大或需要更快的性能,Polars 是一个值得考虑的替代方案。