Polars 和 Pandas 的区别及 Polars 更快速的原因
polars
pola-rs.github.io/polars-book…
主要区别
1. 数据结构
- Pandas:使用
DataFrame和Series数据结构,基于 Python 的原生list和dict实现,适合小到中等规模的数据集。 - Polars:也提供
DataFrame和Series,但使用基于 Rust 的多线程并行计算引擎,适合大数据集。
2. 性能差异
- Pandas:依赖单线程计算,数据结构基于 Python 对象(如
list和dict),在大数据集上性能下降。 - Polars:使用 Rust 语言的并行计算能力,能充分利用现代 CPU 的多核处理,同时采用零拷贝内存处理,减少数据复制,提高性能。
3. 内存管理
- Pandas:由于其基于 Python 对象,内存消耗较大,尤其在处理大数据集时容易出现内存瓶颈。
- Polars:优化了内存管理,使用高效的内存布局,能处理更大的数据集,避免内存问题。
4. API 和功能
- Pandas:功能丰富,成熟,尤其在数据清洗、处理、时间序列分析等方面非常强大。
- Polars:API 较新,功能逐渐完善,支持常见数据操作,但缺少一些 Pandas 的高级功能。
5. 易用性
- Pandas:生态系统广泛,使用者多,文档丰富,学习曲线较低。
- Polars:学习曲线稍高,尤其在高级功能方面,但在处理性能要求高的任务时具有优势。
为什么 Polars 更快?
Polars 的快速性能来自以下几个方面:
-
Rust 的高性能:Polars 使用 Rust 编写,Rust 是高性能的编程语言,提供低级的内存控制和更少的垃圾回收开销,从而提升了性能。
-
并行计算:Polars 默认使用多线程执行数据处理任务,能够充分利用多核 CPU。相比之下,Pandas 大多数操作都是单线程的,限制了处理速度。
-
列式存储:Polars 使用列式存储格式(类似于 Apache Arrow),在执行列操作(如筛选、聚合、排序)时比 Pandas 更高效。
-
内存管理优化:Polars 的内存布局更高效,避免了不必要的数据拷贝和内存分配,使得其性能更加优化。
总结
- Pandas:功能强大、易用,适合常规数据分析,但在大数据集上性能有所下降。
- Polars:更适合处理大数据,性能优越,但功能和生态系统的成熟度相对较低。
如果更注重性能和处理大数据时的效率,Polars。如果需要丰富的功能和更广泛的生态系统支持,Pandas。
附:10000-1000000的数据量测试
import pandas as pd
import polars as pl
import numpy as np
import random
import string
import time
import matplotlib.pyplot as plt
# 随机数据生成
def generate_data(num_rows, num_columns):
"""生成一个包含 num_rows 行和 num_columns 列的随机数据集"""
data = {}
for i in range(num_columns):
column_name = f"col_{i}"
# 随机选择数据类型
if i % 4 == 0:
data[column_name] = np.random.rand(num_rows) # 浮动数值
elif i % 4 == 1:
data[column_name] = np.random.randint(0, 100, num_rows) # 整数
elif i % 4 == 2:
data[column_name] = [random.choice(string.ascii_uppercase) for _ in range(num_rows)] # 字符串
else:
data[column_name] = np.random.choice([True, False], num_rows) # 布尔值
return data
# 生成数据集
num_rows = 10**6 # 1百万行数据
num_columns = 10 # 10列数据
data = generate_data(num_rows, num_columns)
# Pandas 数据集
df_pandas = pd.DataFrame(data)
# Polars 数据集
df_polars = pl.DataFrame(data)
# 定义一个测试函数来执行常见的数据操作
def test_pandas_operations(df):
start = time.time()
df_filtered = df[df['col_0'] > 0.5] # 筛选操作
filter_time = time.time() - start
start = time.time()
df_grouped = df.groupby('col_1').agg({'col_0': 'mean'}) # 分组聚合
group_time = time.time() - start
start = time.time()
df_sorted = df.sort_values(by='col_2') # 排序操作
sort_time = time.time() - start
start = time.time()
df_concat = pd.concat([df, df], axis=0) # 数据连接
concat_time = time.time() - start
start = time.time()
df_sum = df['col_0'].sum() # 数值计算
sum_time = time.time() - start
return filter_time, group_time, sort_time, concat_time, sum_time
def test_polars_operations(df):
start = time.time()
df_filtered = df.filter(pl.col('col_0') > 0.5) # 筛选操作
filter_time = time.time() - start
start = time.time()
df_grouped = df.group_by('col_1').agg(pl.col('col_0').mean()) # 分组聚合
group_time = time.time() - start
start = time.time()
df_sorted = df.sort('col_2') # 排序操作
sort_time = time.time() - start
start = time.time()
df_concat = pl.concat([df, df]) # 数据连接
concat_time = time.time() - start
start = time.time()
df_sum = df.select(pl.col('col_0').sum()) # 数值计算
sum_time = time.time() - start
return filter_time, group_time, sort_time, concat_time, sum_time
# 测试不同数据集规模下的执行时间
row_sizes = [10**4, 10**5, 10**6] # 不同大小的数据集
pandas_times = []
polars_times = []
for rows in row_sizes:
print(f"Testing with {rows} rows...")
# 生成数据
data = generate_data(rows, num_columns)
df_pandas = pd.DataFrame(data)
df_polars = pl.DataFrame(data)
# 测试 Pandas 操作
pandas_results = test_pandas_operations(df_pandas)
pandas_times.append(pandas_results)
# 测试 Polars 操作
polars_results = test_polars_operations(df_polars)
polars_times.append(polars_results)
# 将结果转换为 DataFrame 用于可视化
pandas_times_df = pd.DataFrame(pandas_times, columns=['Filter', 'Group', 'Sort', 'Concat', 'Sum'], index=row_sizes)
polars_times_df = pd.DataFrame(polars_times, columns=['Filter', 'Group', 'Sort', 'Concat', 'Sum'], index=row_sizes)
# 可视化对比速度
fig, axes = plt.subplots(3, 2, figsize=(15, 10))
operations = ['Filter', 'Group', 'Sort', 'Concat', 'Sum']
for i, op in enumerate(operations):
ax = axes[i // 2, i % 2]
ax.plot(row_sizes, pandas_times_df[op], label='Pandas', marker='o')
ax.plot(row_sizes, polars_times_df[op], label='Polars', marker='x')
ax.set_title(f"{op} Time Comparison")
ax.set_xlabel('Number of Rows')
ax.set_ylabel('Time (seconds)')
ax.legend()
plt.tight_layout()
plt.show()