
释放性能:使用 Polars 加速 Pandas 操作
作者 | Ideogram 提供图片
引言
Polars 是目前在单机上处理和操作数据速度最快的开源库之一,其 API 直观且易于使用。它原生于 Rust,旨在处理 DataFrame 时优化内存消耗和速度。
本文将带您了解 Python 中的 Polars 库,并说明如何像使用 Pandas 一样无缝地使用它来高效地操作大型数据集。
设置和数据加载
在整个实际代码示例中,我们将使用一个著名数据集的版本——加州住房数据集,该数据集可在此代码库中找到。这是一个中等大小的数据集,包含了描述加利福尼亚州每个地区房屋和人口特征的数值和分类属性。
如果您是第一次使用 Polars 库,可能需要先安装它
1 |
pip install polars |
如果您在某些 notebook 环境中工作,请记得在上述指令的开头添加“!”。
现在是时候导入 Polars 库并用它来读取数据集了
1 2 3 4 |
import polars as pl url = "https://raw.githubusercontent.com/gakudo-ai/open-datasets/refs/heads/main/housing.csv" df = pl.read_csv(url) |
如您所见,加载数据集的过程与 Pandas 非常相似,函数名也相同,为 read_csv()
。
查看前几行的方法也类似于 Pandas 的对应方法
1 |
print(df.head()) |
但与 Pandas 不同,Polars 提供了一个 DataFrame 属性来查看数据集的 schema,即属性名称及其类型的列表
1 |
print(df.schema) |
输出
Schema([('longitude', Float64), ('latitude', Float64), ('housing_median_age', Float64), ('total_rooms', Float64), ('total_bedrooms', Float64), ('population', Float64), ('households', Float64), ('median_income', Float64), ('median_house_value', Float64), ('ocean_proximity', String)])
检查输出以了解我们将要使用的数据集。
加速数据操作
现在我们已经熟悉了加载的数据集,让我们看看如何使用 Polars 高效地对数据应用各种操作和处理。
以下代码应用了一种缺失值插补策略,使用属性的中位数来填充 total_bedrooms
属性中一些不存在的值
1 2 3 4 |
median_bedrooms = df.select(pl.col("total_bedrooms").median()).item() df = df.with_columns( pl.col("total_bedrooms").fill_null(median_bedrooms) ) |
调用 with_columns()
方法来修改指定的列,即用先前计算出的属性中位数填充缺失值。
接下来,我们来尝试一些Polars 风格的特征工程吧。让我们根据现有特征之间的交互创建一些新特征,以获得每户家庭的房间数、每间房的卧室数以及每户家庭的人口数。
1 2 3 4 5 |
df = df.with_columns([ (pl.col("total_rooms") / pl.col("households")).alias("rooms_per_household"), (pl.col("total_bedrooms") / pl.col("total_rooms")).alias("bedrooms_per_room"), (pl.col("population") / pl.col("households")).alias("population_per_household") ]) |
这里有一点需要特别说明:到目前为止,我们使用的是 Polars 的即时执行模式(eager execution mode),但这个库有两种模式:即时模式和惰性模式。
在即时模式下,数据操作会立即执行。而惰性执行模式(lazy execution mode)则通过使用像 collect()
这样的函数来启用。Polars 的惰性模式,通过使用 lazy()
激活,会在应用任何计算之前优化对该 dataframe 的后续操作序列。这种方法可以使复杂数据处理工作流的执行更加高效。
如果我们回到前面几步,想在惰性模式下执行相同的缺失值插补和特征工程操作,我们会这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ldf = df.lazy() ldf = ldf.with_columns( pl.col("total_bedrooms").fill_null(pl.col("total_bedrooms").median()) ) ldf = ldf.with_columns([ (pl.col("total_rooms") / pl.col("households")).alias("rooms_per_household"), (pl.col("total_bedrooms") / pl.col("total_rooms")).alias("bedrooms_per_room"), (pl.col("population") / pl.col("households")).alias("population_per_household") ]) # 当我们使用 collect() 时,计算才会真正被应用 result_df = ldf.collect() display(result_df.head()) |
执行起来应该会感觉快速、轻量且流畅。
最后,让我们展示一些在惰性模式下进行数据操作的更多示例(虽然下文中没有明确使用,但你可以在任何希望进行计算的地方放置像 result_df = ldf.collect()
和 display(result_df.head())
这样的指令)。
筛选房价中位数高于 50 万美元的地区
1 |
ldf_filtered = ldf.filter(pl.col("median_house_value") > 500000) |
按“海洋邻近度类型”(一个分类属性)对地区进行分组,并计算每组地区的平均房价
1 2 3 |
avg_house_value = ldf.group_by("ocean_proximity").agg( pl.col("median_house_value").mean().alias("avg_house_value") ) |
这里需要注意:在 Polars 的惰性模式下,按类别对实例进行分组的函数是 group_by()
(而不是 groupby()
,注意下划线的使用)。
如果我们试图在没有真正执行操作的情况下访问 avg_house_value
,我们会得到一个已暂存的操作流程的可视化图表

因此,我们必须这样做:
1 2 |
avg_house_value_result = avg_house_value.collect() display(avg_house_value_result) |
总结
Polars 是一个轻量级且高效的替代方案,用于管理类似 Pandas DataFrame 上的复杂数据预处理和清洗工作流。本文通过多个示例展示了如何在 Python 中使用该库的即时和惰性执行模式,从而自定义数据处理流程的规划和执行方式。
暂无评论。