Polars 与大数据量处理
问题
Pandas 处理大数据量时很慢怎么办?Polars 是什么?和 Pandas 有什么区别?
答案
当数据量超过 百万行 级别,Pandas 的性能瓶颈会越来越明显。Polars 是用 Rust 编写的高性能 DataFrame 库,被视为 Pandas 的现代替代方案。
Polars vs Pandas 对比
| 对比 | Pandas | Polars |
|---|---|---|
| 语言 | C/Python | Rust |
| 性能 | 基线 | 快 5~20 倍 |
| 内存 | 较高 | 更低(列式+压缩) |
| 并行 | 单线程 | 自动多线程 |
| 惰性执行 | ❌ | ✅(LazyFrame) |
| API 风格 | 命令式 | 表达式(声明式) |
| 生态 | 极丰富 | 快速增长 |
| 学习成本 | 低 | 中(新 API) |
面试建议
Polars 在数据分析面试中还不是必考项,但提到大数据量优化时能说出 Polars 是加分项。
Polars 基础操作
Polars 快速入门
import polars as pl
# 创建 DataFrame
df = pl.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'age': [25, 30, 35],
'salary': [15000, 20000, 25000]
})
# 读取 CSV / Parquet
df = pl.read_csv('data.csv')
df = pl.read_parquet('data.parquet')
# 选择列
df.select('name', 'age')
df.select(pl.col('name'), pl.col('age'))
# 筛选行
df.filter(pl.col('age') > 25)
df.filter((pl.col('age') > 25) & (pl.col('salary') > 18000))
# 新增列
df.with_columns(
(pl.col('salary') * 0.1).alias('bonus'),
pl.col('name').str.to_lowercase().alias('name_lower')
)
# 聚合
df.group_by('name').agg(
pl.col('salary').mean().alias('avg_salary'),
pl.col('age').max().alias('max_age')
)
# 排序
df.sort('salary', descending=True)
LazyFrame — 惰性执行
Polars 最强大的特性——查询计划优化:
LazyFrame(惰性执行)
# Eager 模式(立即执行)
result = df.filter(pl.col('age') > 25).select('name', 'salary')
# Lazy 模式(延迟执行,自动优化)
result = (
df.lazy() # 转为 LazyFrame
.filter(pl.col('age') > 25) # 不立即执行
.select('name', 'salary') # 不立即执行
.collect() # 触发执行 + 查询优化
)
# scan 直接以 Lazy 模式读取文件(最佳实践)
result = (
pl.scan_csv('data.csv') # 不读入全部数据
.filter(pl.col('city') == '北京') # 谓词下推:读取时就过滤
.select('name', 'salary') # 投影下推:只读需要的列
.collect()
)
LazyFrame 的优化
- 谓词下推:过滤条件尽早执行,减少处理的数据量
- 投影下推:只读取需要的列
- 公共子表达式消除:避免重复计算
- 并行执行:自动利用多核 CPU
Pandas → Polars 常用操作对照
| 操作 | Pandas | Polars |
|---|---|---|
| 选列 | df[['a', 'b']] | df.select('a', 'b') |
| 筛选 | df[df['a'] > 1] | df.filter(pl.col('a') > 1) |
| 新增列 | df['c'] = df['a'] * 2 | df.with_columns((pl.col('a') * 2).alias('c')) |
| 分组聚合 | df.groupby('a').agg({'b': 'mean'}) | df.group_by('a').agg(pl.col('b').mean()) |
| 排序 | df.sort_values('a') | df.sort('a') |
| 合并 | pd.merge(df1, df2, on='key') | df1.join(df2, on='key') |
| 去重 | df.drop_duplicates() | df.unique() |
大数据量处理策略
当 Pandas 不够快时,按数据规模选择方案:
| 数据规模 | 推荐方案 |
|---|---|
| < 100 万行 | Pandas 直接处理 |
| 100 万 ~ 1000 万行 | Pandas 优化 / Polars |
| 1000 万 ~ 1 亿行 | Polars LazyFrame |
| > 1 亿行 | Spark / Hive / 数据库 |
Pandas 大数据优化技巧
Pandas 处理大数据量的优化
import pandas as pd
# 1. 使用 Parquet 而非 CSV(快 5~10 倍)
df = pd.read_parquet('data.parquet')
# 2. 只读需要的列
df = pd.read_csv('data.csv', usecols=['col1', 'col2'])
# 3. 指定数据类型减少内存
df = pd.read_csv('data.csv', dtype={
'id': 'int32', # int64 → int32
'price': 'float32', # float64 → float32
'city': 'category' # str → category
})
# 4. 分块读取
chunks = pd.read_csv('big.csv', chunksize=500000)
results = []
for chunk in chunks:
processed = chunk[chunk['amount'] > 0].groupby('city')['amount'].sum()
results.append(processed)
final = pd.concat(results).groupby(level=0).sum()
常见面试问题
Q1: 什么时候用 Polars 替代 Pandas?
答案:
- 数据量 > 百万行,Pandas 明显变慢
- 需要自动多线程并行
- 内存受限(Polars 更省内存)
- 即使数据量不大,Polars 的惰性执行也能优化查询效率
不适合用 Polars:需要丰富第三方库生态时(如 statsmodels、sklearn 接受 Pandas 输入)
Q2: Polars 的 LazyFrame 有什么优势?
答案:
- 查询优化:自动执行谓词下推、投影下推等优化
- 减少内存:scan 模式不需要一次性加载全部数据
- 自动并行:利用所有 CPU 核心
- 类似 Spark 的执行计划优化,但在单机上运行
Q3: Parquet 格式为什么比 CSV 快?
答案:
- 列式存储:只读需要的列,不用扫描整行
- 内置压缩:文件体积小 5~10 倍
- 类型信息:不需要推断类型,直接加载
- 二进制格式:无需解析文本
- 推荐在数据管道中使用 Parquet 替代 CSV