来源:投稿 作者:sunny
编辑:学姐
unset
unset
Polars简介
unset
unset
Polars 是一个速度极快的 DataFrame 库,用于处理结构化数据。核心是用 Rust 编写的,可用于 Python、R 和 NodeJS。
对于Polar而言,它有着如下特点:
-
快速:用 Rust 从头开始编写,设计接近机器,没有外部依赖。
-
I/O:对所有常见数据存储层的一流支持:本地、云存储和数据库。
-
直观的 API:按预期方式编写查询。Polars 将在内部确定使用其查询优化器执行的最有效方式。
-
Out of Core:流式处理 API 允许您处理结果,而无需所有数据同时在内存中。
-
并行:通过在可用的 CPU 内核之间分配工作负载来利用计算机的强大功能,而无需任何额外的配置。
-
矢量化查询引擎:使用 Apache Arrow(一种列式数据格式)以矢量化方式处理查询,并使用 SIMD 优化 CPU 使用率。
Polars 的目标是提供一个闪电般快速的 DataFrame 库,该库:
Polars 是用 Rust 编写的,这赋予了它 C/C++ 性能,并允许它完全控制查询引擎中的性能关键部分。
unset
unset
Polars安装和使用
unset
unset
在正式讲述这个库之前,我们先来安装该库。
pip install polars
读取/写入文件
Polars 支持常见文件格式(e.g. csv、json、parquet)、云存储(S3、Azure Blob、BigQuery)和数据库(如 postgres、mysql)的读写。
import polars as pl
from datetime import datetime
df = pl.DataFrame(
{
"integer": [1, 2, 3],
"date": [
datetime(2025, 1, 1),
datetime(2025, 1, 2),
datetime(2025
, 1, 3),
],
"float": [4.0, 5.0, 6.0],
"string": ["a", "b", "c"],
}
)
print(df)
# 结果如下:
shape: (3, 4)
┌─────────┬─────────────────────┬───────┬────────┐
│ integer ┆ date ┆ float ┆ string │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ datetime[μs] ┆ f64 ┆ str │
╞═════════╪═════════════════════╪═══════╪════════╡
│ 1 ┆ 2025-01-01 00:00:00 ┆ 4.0 ┆ a │
│ 2 ┆ 2025-01-02 00:00:00 ┆ 5.0 ┆ b │
│ 3 ┆ 2025-01-03 00:00:00 ┆ 6.0 ┆ c │
└─────────┴─────────────────────┴───────┴────────┘
在下面的示例中,我们将 DataFrame 写入一个名为
output.csv
的 csv 文件。之后,我们用
read_csv
回读它,然后
print
用结果进行检查。
df.write_csv("docs/data/output.csv")
df_csv = pl.read_csv("docs/data/output.csv")
print(df_csv)
# 结果如下:
shape: (3, 4)
┌─────────┬────────────────────────────┬───────┬────────┐
│ integer ┆ date ┆ float ┆ string │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str │
╞═════════╪════════════════════════════╪═══════╪════════╡
│ 1 ┆ 2025-01-01T00:00:00.000000 ┆ 4.0 ┆ a │
│ 2 ┆ 2025-01-02T00:00:00.000000 ┆ 5.0 ┆ b │
│ 3 ┆ 2025-01-03T00:00:00.000000 ┆ 6.0 ┆ c │
└─────────┴────────────────────────────┴───────┴────────┘
表达式
Expressions
是极地的核心力量。它
expressions
提供了一个模块化结构,允许您将简单的概念组合到复杂的查询中。下面我们将介绍作为所有查询的构建块(或在 Polars 术语上下文中)的基本组件:
Select选择
要选择列,我们需要做两件事:
在下面的示例中,您会看到我们选择
col('*')
。星号代表所有列。
df.select(pl.col("*"))
# 结果如下:
shape: (5, 4)
┌─────┬──────────┬─────────────────────┬───────┐
│ a ┆ b ┆ c ┆ d │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ datetime[μs] ┆ f64 │
╞═════╪══════════╪═════════════════════╪═══════╡
│ 0 ┆ 0.337071 ┆ 2025-12-01 00:00:00 ┆ 1.0 │
│ 1 ┆ 0.930258 ┆ 2025-12-02 00:00:00 ┆ 2.0 │
│ 2 ┆ 0.24564 ┆ 2025-12-03 00:00:00 ┆ NaN │
│ 3 ┆ 0.673248 ┆ 2025-12-04 00:00:00 ┆ -42.0 │
│ 4 ┆ 0.460631 ┆ 2025-12-05
00:00:00 ┆ null │
└─────┴──────────┴─────────────────────┴───────┘
您还可以指定要返回的特定列。有两种方法可以做到这一点。第一个选项是传递列名称,如下所示。
df.select(pl.col("a", "b"))
# 结果如下:
shape: (5, 2)
┌─────┬──────────┐
│ a ┆ b │
│ --- ┆ --- │
│ i64 ┆ f64 │
╞═════╪══════════╡
│ 0 ┆ 0.337071 │
│ 1 ┆ 0.930258 │
│ 2 ┆ 0.24564 │
│ 3 ┆ 0.673248 │
│ 4 ┆ 0.460631 │
└─────┴──────────┘
Filter过滤器
该
filter
选项允许我们创建
DataFrame
.我们使用与之前相同的
DataFrame
方法,并在两个指定日期之间进行过滤。
df.filter(
pl.col("c").is_between(datetime(2025, 12, 2), datetime(2025, 12, 3)),
)
# 结果如下:
shape: (2, 4)
┌─────┬──────────┬─────────────────────┬─────┐
│ a ┆ b ┆ c ┆ d │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ datetime[μs] ┆ f64 │
╞═════╪══════════╪═════════════════════╪═════╡
│ 1 ┆ 0.930258 ┆ 2025-12-02 00:00:00 ┆ 2.0 │
│ 2 ┆ 0.24564 ┆ 2025-12-03 00:00:00 ┆ NaN │
└─────┴──────────┴─────────────────────┴─────┘
您还可以
filter
创建包含多个列的更复杂的过滤器。
df.filter((pl.col("a") <= 3) & (pl.col("d").is_not_nan()))
# 结果如下:
shape: (3, 4)
┌─────┬──────────┬─────────────────────┬───────┐
│ a ┆ b ┆ c ┆ d │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ datetime[μs] ┆ f64 │
╞═════╪══════════╪═════════════════════╪═══════╡
│ 0 ┆ 0.337071 ┆ 2025-12-01 00:00:00 ┆ 1.0 │
│ 1 ┆ 0.930258 ┆ 2025-12-02 00:00:00 ┆ 2.0 │
│ 3 ┆ 0.673248 ┆ 2025-12-04 00:00:00 ┆ -42.0 │
└─────┴──────────┴─────────────────────┴───────┘
添加列Add columns
with_columns
允许您为分析创建新列。我们创建两个新列
e
和
b+42
.首先,我们将列
b
中的所有值相加,并将结果存储在列
e
中。之后,我们添加
42
的
b
值。创建一个新列
b+42
来存储这些结果。
df.with_columns(pl.col("b").sum().alias("e"), (pl.col("b") + 42).alias("b+42"))
# 结果如下:
shape: (5, 6)
┌─────┬──────────┬─────────────────────┬───────┬──────────┬───────────┐
│ a ┆ b ┆ c ┆ d ┆ e ┆ b+42 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ datetime[μs] ┆ f64 ┆ f64 ┆ f64 │
╞═════╪══════════╪═════════════════════╪═══════╪══════════╪═══════════╡
│ 0 ┆ 0.337071 ┆ 2025-12-01 00:00:00
┆ 1.0 ┆ 2.646848 ┆ 42.337071 │
│ 1 ┆ 0.930258 ┆ 2025-12-02 00:00:00 ┆ 2.0 ┆ 2.646848 ┆ 42.930258 │
│ 2 ┆ 0.24564 ┆ 2025-12-03 00:00:00 ┆ NaN ┆ 2.646848 ┆ 42.24564 │
│ 3 ┆ 0.673248 ┆ 2025-12-04 00:00:00 ┆ -42.0 ┆ 2.646848 ┆ 42.673248 │
│ 4 ┆ 0.460631 ┆ 2025-12-05 00:00:00 ┆ null ┆ 2.646848 ┆ 42.460631 │
└─────┴──────────┴─────────────────────┴───────┴──────────┴───────────┘
分组依据Group by
我们将按功能为组创建一个新
DataFrame
功能。这个新内容
DataFrame
将包括我们想要分组的几个“组”。
df2 = pl.DataFrame(
{
"x": range(8),
"y": ["A", "A", "A", "B", "B", "C", "X", "X"],
}
)
# 结果如下:
shape: (8, 2)
┌─────┬─────┐
│ x ┆ y │
│ --- ┆ --- │
│ i64 ┆ str │
╞═════╪═════╡
│ 0 ┆ A │
│ 1 ┆ A │
│ 2 ┆ A │
│ 3 ┆ B │
│ 4 ┆ B │
│ 5 ┆ C │
│ 6 ┆ X │
│ 7 ┆ X │
└─────┴─────┘
df2.group_by("y", maintain_order=True).len()
# 结果如下:
shape: (4, 2)
┌─────┬─────┐
│ y ┆ len │
│ --- ┆ --- │
│ str ┆ u32 │
╞═════╪═════╡
│ A ┆ 3 │
│ B ┆ 2 │
│ C ┆ 1 │
│ X ┆ 2 │
└─────┴─────┘
df2.group_by("y", maintain_order=True).agg(
pl.col("*").count().alias("count"),
pl.col("*").sum().alias("sum"),
)
# 结果如下:
shape: (4, 3)
┌─────┬───────┬─────┐
│ y ┆ count ┆ sum │
│ --- ┆ --- ┆ --- │
│ str ┆ u32 ┆ i64 │
╞═════╪═══════╪═════╡
│ A ┆ 3 ┆ 3 │
│ B ┆ 2 ┆ 7 │
│ C ┆ 1 ┆ 5 │
│ X ┆ 2 ┆ 13 │
└─────┴───────┴─────┘
组合Combination
下面是有关如何组合操作以创建
DataFrame
所需的操作的一些示例。
df_x = df.with_columns((pl.col("a") * pl.col("b")).alias("a * b")).select(
pl.all().exclude(["c", "d"])
)
print(df_x)
# 结果如下:
shape: (5, 3)
┌─────┬──────────┬──────────┐
│ a ┆ b ┆ a * b │
│ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ f64 │
╞═════╪══════════╪══════════╡
│ 0 ┆ 0.337071 ┆ 0.0 │
│ 1 ┆ 0.930258 ┆ 0.930258 │
│ 2 ┆ 0.24564 ┆ 0.491279 │
│ 3 ┆ 0.673248 ┆ 2.019744 │
│ 4 ┆ 0.460631 ┆ 1.842525 │
└─────┴──────────┴──────────┘
df_y = df.with_columns((pl.col("a") * pl.col("b")).alias("a * b")).select(
pl.all().exclude("d")
)
print(df_y)
# 结果如下:
shape: (5, 4)
┌─────┬──────────┬─────────────────────┬──────────┐
│ a ┆ b ┆ c ┆ a * b │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ datetime[μs] ┆ f64 │
╞═════╪══════════╪═════════════════════╪══════════╡
│ 0 ┆ 0.337071 ┆ 2025-12-01 00:00:00 ┆ 0.0 │
│ 1 ┆ 0.930258 ┆ 2025-12-02 00:00:00 ┆ 0.930258 │
│ 2 ┆ 0.24564 ┆ 2025-12-03 00:00:00 ┆ 0.491279 │
│ 3 ┆ 0.673248 ┆ 2025-12-04 00:00:00 ┆ 2.019744 │
│ 4 ┆ 0.460631 ┆ 2025-12-05 00:00:00 ┆ 1.842525 │
└─────┴──────────┴─────────────────────┴──────────┘
合并DataFrame
根据用例,有两种方法可以
DataFrame
组合 s:join 和 concat。
Join
Polars 支持所有类型的连接(例如左连接、右连接、内连接、外连接)。让我们仔细看看如何
join
将两个
DataFrames
合并为一个
DataFrame
。我们两个
DataFrames
都有一个类似“id”的列:
a
和
x
。
DataFrames
在此示例中,我们可以将
join
这些列用于 。
df = pl.DataFrame(
{
"a": range(8),
"b": np.random.rand(8),
"d": [1, 2.0, float("nan"), float("nan"), 0, -5, -42, None],
}
)
df2 = pl.DataFrame(
{
"x": range(8),
"y": ["A", "A", "A", "B", "B", "C", "X", "X"],
}
)
joined = df.join(df2, left_on="a", right_on="x")
print(joined)
# 结果如下:
shape: (8, 4)
┌─────┬──────────┬───────┬─────┐
│ a ┆ b ┆ d ┆ y │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ f64 ┆ str │
╞═════╪══════════╪═══════╪═════╡
│ 0 ┆ 0.371542 ┆ 1.0 ┆ A │
│ 1 ┆ 0.748599 ┆ 2.0 ┆ A │
│ 2 ┆ 0.490039 ┆ NaN ┆ A │
│ 3 ┆ 0.292075 ┆ NaN ┆ B │
│ 4 ┆ 0.227413 ┆ 0.0 ┆ B │
│ 5 ┆ 0.410372 ┆ -5.0 ┆ C │
│ 6 ┆ 0.549948 ┆ -42.0 ┆ X │
│ 7 ┆ 0.48205 ┆ null ┆ X │
└─────┴──────────┴───────┴─────┘
Concat
我们也可以
concatenate
两个
DataFrames
.垂直串联将使
DataFrame
时间更长。水平串联将使
DataFrame
宽度更大。下面你可以看到我们两个
DataFrames
的水平串联的结果。
stacked = df.hstack(df2)
print(stacked)
# 结果如下:
shape: (8, 5)
┌─────┬──────────┬───────┬─────┬─────┐
│ a ┆ b ┆ d ┆ x ┆ y │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ f64 ┆ i64 ┆ str │
╞═════╪══════════╪═══════╪═════╪═════╡
│ 0 ┆ 0.371542 ┆ 1.0 ┆ 0 ┆ A │
│ 1 ┆ 0.748599 ┆ 2.0 ┆ 1 ┆ A │
│ 2 ┆ 0.490039 ┆ NaN ┆ 2 ┆ A │
│ 3 ┆ 0.292075 ┆ NaN ┆ 3 ┆ B │
│ 4 ┆ 0.227413 ┆ 0.0 ┆ 4 ┆ B │
│ 5 ┆ 0.410372 ┆ -5.0 ┆ 5 ┆ C │
│ 6 ┆ 0.549948 ┆ -42.0 ┆ 6 ┆ X │
│ 7 ┆ 0.48205 ┆ null ┆ 7 ┆ X │
└─────┴──────────┴───────┴─────┴─────┘
unset
unset
数据结构
unset
unset
Polars 提供的核心基础数据结构是
Series
和
DataFrame
。
Series
Series是一维数据结构。在序列中,所有元素都具有相同的数据类型。下面的代码片段显示了如何创建简单的命名
Series
对象。
import polars as pl
s = pl.Series("a", [1, 2, 3, 4, 5])
print(s)
# 结果如下:
shape: (5,)
Series: 'a' [i64]
[
1
2
3
4
5
]
DataFrame
A
DataFrame
是一个由 支持
Series
的二维数据结构,它可以看作是 的集合(例如列表)的
Series
抽象。可以对 执行
DataFrame
的操作与
SQL
在类似查询中执行的操作非常相似。您可以
GROUP BY
、
JOIN
、
PIVOT
定义自定义函数。
from datetime import datetime
df = pl.DataFrame(
{
"integer": [1, 2, 3, 4, 5],
"date": [
datetime(2022, 1, 1),
datetime(2022, 1, 2),
datetime(2022, 1, 3),
datetime(2022, 1, 4),
datetime(2022, 1, 5),
],
"float": [4.0, 5.0, 6.0, 7.0, 8.0],
}
)
print(df)
# 结果如下:
shape: (5, 3)
┌─────────┬─────────────────────┬───────┐
│ integer ┆ date ┆ float │
│ --- ┆ --- ┆ --- │
│ i64 ┆ datetime[μs] ┆ f64 │
╞═════════╪═════════════════════╪═══════╡
│ 1 ┆ 2022-01-01 00:00:00 ┆ 4.0 │
│ 2 ┆ 2022-01-02 00:00:00 ┆ 5.0 │
│ 3 ┆ 2022-01-03 00:00:00 ┆ 6.0 │
│ 4 ┆ 2022-01-04 00:00:00 ┆ 7.0 │
│ 5 ┆ 2022-01-05 00:00:00 ┆ 8.0 │
└─────────┴─────────────────────┴───────┘
查看数据
本部分重点介绍如何
DataFrame
查看 .我们将使用上一个示例中的 作为
DataFrame
起点。
head
默认情况下,该
head
函数显示 .
DataFrame
您可以指定要查看的行数(例如)。
df.head(10)
print(df.head(3))
# 结果如下:
shape: (3, 3)
┌─────────┬─────────────────────┬───────┐
│ integer ┆ date ┆ float │
│ --- ┆ --- ┆ --- │
│ i64 ┆ datetime[μs] ┆ f64 │
╞═════════╪═════════════════════╪═══════╡
│ 1 ┆ 2022-01-01 00:00:00 ┆ 4.0 │
│ 2 ┆ 2022-01-02 00:00:00 ┆ 5.0 │
│ 3 ┆ 2022-01-03 00:00:00 ┆ 6.0 │
└─────────┴─────────────────────┴───────┘
tail
该
tail
函数显示 .
DataFrame
您还可以指定要查看的行数,类似于
head
。
print(df.tail(3))
# 结果如下:
shape: (3, 3)
┌─────────┬─────────────────────┬───────┐
│ integer ┆ date ┆ float │
│ --- ┆ --- ┆ --- │
│ i64 ┆ datetime[μs] ┆ f64 │
╞═════════╪═════════════════════╪═══════╡
│ 3 ┆ 2022-01-03 00:00:00 ┆ 6.0 │
│ 4 ┆ 2022-01-04 00:00:00 ┆ 7.0 │
│ 5 ┆ 2022-01-05 00:00:00 ┆ 8.0 │
└─────────┴─────────────────────┴───────┘
sample
如果你想得到你
DataFrame
的数据的印象,你也可以使用
sample
。从
sample
中得到
DataFrame
n 个随机行。
print(df.sample(2))
# 结果如下:
shape: (2, 3)
┌─────────┬─────────────────────┬───────┐
│ integer ┆ date ┆ float │
│ --- ┆ --- ┆ --- │
│ i64 ┆ datetime[μs] ┆ f64 │
╞═════════╪═════════════════════╪═══════╡
│ 5 ┆ 2022-01-05 00:00:00 ┆ 8.0 │
│ 1 ┆ 2022-01-01 00:00:00 ┆ 4.0 │
describe
Describe
返回 .
DataFrame
如果可能,它将提供一些快速统计信息。
print(df.describe())
# 结果如下:
shape: (9, 4)
┌────────────┬──────────┬─────────────────────┬──────────┐
│ statistic ┆ integer ┆ date ┆ float │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ str ┆ f64 │
╞════════════╪══════════╪═════════════════════╪══════════╡
│ count ┆ 5.0 ┆ 5 ┆ 5.0 │
│ null_count ┆ 0.0 ┆ 0 ┆ 0.0 │
│ mean ┆ 3.0 ┆ 2022-01-03 00:00:00 ┆ 6.0 │
│ std ┆ 1.581139 ┆ null ┆ 1.581139 │
│ min ┆ 1.0 ┆ 2022-01-01 00:00:00 ┆ 4.0 │
│ 25% ┆ 2.0 ┆ 2022-01-02 00:00:00 ┆ 5.0 │
│ 50% ┆ 3.0 ┆ 2022-01-03 00:00:00 ┆ 6.0 │
│ 75% ┆ 4.0 ┆ 2022-01-04 00:00:00 ┆ 7.0 │
│ max ┆ 5.0 ┆ 2022-01-05 00:00:00 ┆ 8.0 │
└────────────┴──────────┴─────────────────────┴──────────┘
unset
unset
上下文
unset
unset
Polars 开发了自己的领域特定语言 (DSL) 来转换数据。该语言非常易于使用,并允许复杂的查询保持人类可读性。该语言的两个核心组件是上下文和表达式,后者我们将在下一节中介绍。
顾名思义,上下文是指需要计算表达式的上下文。主要有三种情况:
-
Selection:
df.select(...)
,
df.with_columns(...)
-
-
Group by / Aggregation:
df.group_by(...).agg(...)
以下示例在以下
DataFrame
情况下执行:
df = pl.DataFrame(
{
"nrs": [1, 2, 3, None, 5],
"names": ["foo", "ham", "spam", "egg", None],
"random": np.random.rand(5),
"groups": ["A", "A", "B", "C", "B"],
}
)
print(df)
# 结果如下:
shape: (5, 4)
┌──────┬───────┬──────────┬────────┐
│ nrs ┆ names ┆ random ┆ groups │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str │
╞══════╪═══════╪══════════╪════════╡
│ 1 ┆ foo ┆ 0.154163 ┆ A │
│ 2 ┆ ham ┆ 0.74005 ┆ A │
│ 3 ┆ spam ┆ 0.263315 ┆ B │
│ null ┆ egg ┆ 0.533739 ┆ C │
│ 5 ┆ null ┆ 0.014575 ┆ B │
└──────┴───────┴──────────┴────────┘
Selection
选择上下文将表达式应用于列。
select
可能会生成聚合、表达式组合或文本的新列。
选择上下文中的表达式必须生成
Series
长度相同或长度为 1 的表达式。文本被视为 length-1
Series
。
out = df.select(
pl.sum("nrs"),
pl.col("names").sort(),
pl.col("names").first().alias("first name"),
(pl.mean("nrs") * 10).alias("10xnrs"),
)
print(out)
# 结果如下:
shape: (5, 4)
┌─────┬───────┬────────────┬────────┐
│ nrs ┆ names ┆ first name ┆ 10xnrs │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str ┆ f64 │
╞═════╪═══════╪════════════╪════════╡
│ 11 ┆ null ┆ foo ┆ 27.5 │
│ 11 ┆ egg ┆ foo ┆ 27.5 │
│ 11 ┆ foo ┆ foo ┆ 27.5 │
│ 11 ┆ ham ┆ foo ┆ 27.5 │
│ 11 ┆ spam ┆ foo ┆ 27.5 │
└─────┴───────┴────────────┴────────┘
从查询中可以看出,选择上下文非常强大,允许您计算彼此独立(并行)的任意表达式。
与
select
语句类似,该
with_columns
语句也进入选择上下文。和
select
之间的
with_columns
主要区别在于保留
with_columns
原始列并添加新列,而
select
删除原始列。
df = df.with_columns(
pl.sum("nrs").alias("nrs_sum"),
pl.col("random").count().alias("count"),
)
print(df)
# 结果如下:
shape: (5, 6)
┌──────┬───────┬──────────┬────────┬─────────┬───────┐
│ nrs ┆ names ┆ random ┆ groups ┆ nrs_sum ┆ count │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str ┆ i64 ┆ u32 │
╞══════╪═══════╪══════════╪════════╪═════════╪═══════╡
│ 1 ┆ foo ┆ 0.154163 ┆ A ┆ 11 ┆ 5 │
│ 2 ┆ ham ┆ 0.74005 ┆ A ┆ 11 ┆ 5 │
│ 3 ┆ spam ┆ 0.263315 ┆ B ┆ 11 ┆ 5 │
│ null ┆ egg ┆ 0.533739 ┆ C ┆ 11 ┆ 5 │
│ 5 ┆ null ┆ 0.014575 ┆ B ┆ 11 ┆ 5 │
└──────┴───────┴──────────┴────────┴─────────┴───────┘
Filtering
筛选上下文根据计算结果为
Boolean
数据类型的一个或多个表达式筛选 a
DataFrame
。
out = df.filter(pl.col("nrs") > 2)
print(out)
# 结果如下:
shape: (2, 6)
┌─────┬───────┬──────────┬────────┬─────────┬───────┐
│ nrs ┆ names ┆ random ┆ groups ┆ nrs_sum ┆ count │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str ┆ i64 ┆ u32 │
╞═════╪═══════╪══════════╪════════╪═════════╪═══════╡
│ 3 ┆ spam ┆ 0.263315 ┆ B ┆ 11 ┆ 5 │
│ 5 ┆ null ┆ 0.014575 ┆ B ┆ 11 ┆ 5 │
└─────┴───────┴──────────┴────────┴─────────┴───────┘
Group by
在
group_by
上下文中,表达式适用于组,因此可以产生任何长度的结果(一个组可能有许多成员)。
out = df.group_by("groups").agg(
pl.sum("nrs"), # sum nrs by groups
pl.col("random").count().alias("count"), # count group members
# sum random where name != null
pl.col("random").filter(pl.col("names").is_not_null()).sum().name.suffix("_sum"),
pl.col("names").reverse().alias("reversed names"),
)
print(out)
# 结果如下:
shape: (3, 5)
┌────────┬─────┬───────┬────────────┬────────────────┐
│ groups ┆ nrs ┆ count ┆ random_sum ┆ reversed names │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ u32 ┆ f64 ┆ list[str] │
╞════════╪═════╪═══════╪════════════╪════════════════╡
│ B ┆ 8 ┆ 2 ┆ 0.263315 ┆ [null, "spam"] │
│ A ┆ 3 ┆ 2 ┆ 0.894213 ┆ ["ham", "foo"] │
│ C ┆ 0 ┆ 1 ┆ 0.533739 ┆ ["egg"] │
└────────┴─────┴───────┴────────────┴────────────────┘
从结果中可以看出,所有表达式都应用于上下文定义的
group_by
组。除了标准
group_by
、
group_by_dynamic
和
group_by_rolling
之外,还通过上下文进入该组。
unset
unset
表达式
unset
unset
Polars 有一个强大的概念,称为表达式,这是其非常快速的性能的核心。
表达式是描述如何构造一个或多个系列的操作树。由于输出是 Series,因此可以直接应用一系列表达式(类似于 pandas 中的方法链),每个表达式都会转换上一步的输出。
下面是一个表达式:
pl.col("foo").sort().head(2)
选择列“foo”,然后对列进行排序(不按相反的顺序排列),取排序输出的前两个值。
表达式的力量在于,每个表达式都会产生一个新的表达式,并且它们可以通过管道连接在一起。您可以通过将表达式传递给其中一个 Polars 执行上下文来运行表达式。
在这里,我们通过运行
df.select
以下命令来运行两个表达式:
df.select(pl.col("foo").sort().head(2), pl.col("bar").filter(pl.col("foo") == 1).sum())
所有表达式都是并行运行的,这意味着单独的 Polars 表达式是令人尴尬的并行。请注意,在表达式中可能会进行更多的并行化。
在前面中,我们概述了什么是
Expressions
以及它们如何是无价的。在本节中,我们将重点介绍
Expressions
它们本身。每个部分都概述了它们的作用,并提供了其他示例。
基本运算符
本节介绍如何将基本运算符(例如加法、减法)与表达式结合使用。我们将在以下数据帧的上下文中使用不同的主题提供各种示例。
df = pl.DataFrame(
{
"nrs": [1, 2, 3, None, 5],
"names": ["foo", "ham", "spam", "egg", None],
"random": np.random.rand(5),
"groups": ["A", "A", "B", "C", "B"],
}
)
print(df)
# 结果如下:
shape: (5, 4)
┌──────┬───────┬──────────┬────────┐
│ nrs ┆ names ┆ random ┆ groups │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str │
╞══════╪═══════╪══════════╪════════╡
│ 1 ┆ foo ┆ 0.154163 ┆ A │
│ 2 ┆ ham ┆ 0.74005 ┆ A │
│ 3 ┆ spam ┆ 0.263315 ┆ B │
│ null ┆ egg ┆ 0.533739 ┆ C │
│ 5 ┆ null ┆ 0.014575 ┆ B │
└──────┴───────┴──────────┴────────┘
Numerical
df_numerical = df.select(
(pl.col("nrs") + 5).alias("nrs + 5"),
(pl.col("nrs") - 5).alias("nrs - 5"),
(pl.col("nrs") * pl.col("random")).alias("nrs * random"),
(pl.col("nrs") / pl.col("random")).alias("nrs / random"),
)
print(df_numerical)
# 结果如下:
shape: (5, 4)
┌─────────┬─────────┬──────────────┬──────────────┐
│ nrs + 5 ┆ nrs - 5 ┆ nrs * random ┆ nrs / random │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ f64 ┆ f64 │
╞═════════╪═════════╪══════════════╪══════════════╡
│ 6 ┆ -4 ┆ 0.154163 ┆ 6.486647 │
│ 7 ┆ -3 ┆ 1.480099 ┆ 2.702521 │
│ 8 ┆ -2 ┆ 0.789945 ┆ 11.393198 │
│ null ┆ null ┆ null ┆ null │
│ 10 ┆ 0 ┆ 0.072875 ┆ 343.054056 │
└─────────┴─────────┴──────────────┴──────────────┘
Logical
df_logical = df.select(
(pl.col("nrs") > 1).alias("nrs > 1"),
(pl.col("random") <= 0.5).alias("random <= .5"),
(pl.col("nrs") != 1).alias("nrs != 1"),
(pl.col("nrs") == 1).alias("nrs == 1"),
((pl.col("random") <= 0.5) & (pl.col("nrs") > 1)).alias("and_expr"), # and
((pl.col("random") <= 0.5) | (pl.col("nrs") > 1)).alias("or_expr"), # or
)
print(df_logical)
# 结果如下:
shape: (5, 6)
┌─────────┬──────────────┬──────────┬──────────┬──────────┬─────────┐
│ nrs > 1 ┆ random <= .5 ┆ nrs != 1 ┆ nrs == 1 ┆ and_expr ┆ or_expr │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ bool ┆ bool ┆ bool ┆ bool ┆ bool ┆ bool │
╞═════════╪══════════════╪══════════╪══════════╪══════════╪═════════╡
│ false ┆ true ┆ false ┆ true ┆ false ┆ true │
│ true ┆ false ┆ true ┆ false ┆ false ┆ true │
│ true ┆ true ┆ true ┆ false ┆ true ┆ true │
│ null ┆ false ┆ null ┆ null ┆ false ┆ null │
│ true ┆ true ┆ true ┆ false ┆ true ┆ true │
└─────────┴──────────────┴──────────┴──────────┴──────────┴─────────┘
列选择
让我们创建一个数据集以在本节中使用:
from datetime import date, datetime
import polars as pl
df = pl.DataFrame(
{
"id": [9, 4, 2],
"place": ["Mars", "Earth", "Saturn"],
"date": pl.date_range(date(2022, 1, 1), date(2022, 1, 3), "1d", eager=True),
"sales": [33.4, 2142134.1, 44.7],
"has_people": [False, True, False],
"logged_at": pl.datetime_range(
datetime(2022, 12, 1), datetime(2022, 12, 1, 0, 0, 2), "1s", eager=True
),
}
).with_row_index("index")
print(df)
# 结果如下:
shape: (3, 7)
┌───────┬─────┬────────┬────────────┬───────────┬────────────┬─────────────────────┐
│ index ┆ id ┆ place ┆ date ┆ sales ┆ has_people ┆ logged_at │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ u32 ┆ i64 ┆ str ┆ date ┆ f64 ┆ bool ┆ datetime[μs] │
╞═══════╪═════╪════════╪════════════╪═══════════╪════════════╪═════════════════════╡
│ 0 ┆ 9 ┆ Mars ┆ 2022-01-01 ┆ 33.4 ┆ false ┆ 2022-12-01 00:00:00 │
│ 1 ┆ 4 ┆ Earth ┆ 2022-01-02 ┆ 2142134.1 ┆ true ┆ 2022-12-01 00:00:01 │
│ 2 ┆ 2 ┆ Saturn ┆ 2022-01-03 ┆ 44.7 ┆ false ┆ 2022-12-01 00:00:02 │
└───────┴─────┴────────┴────────────┴───────────┴────────────┴─────────────────────┘
函数
Polars 表达式具有大量内置函数。这些允许您创建复杂的查询,而无需用户定义的函数。这里要介绍的太多了,但我们将介绍一些更流行的用例。如果要查看所有函数,请转到编程语言的 API 参考。
df = pl.DataFrame(
{
"nrs": [1, 2, 3, None, 5],
"names": ["foo", "ham", "spam", "egg", "spam"],
"random": np.random.rand(5),
"groups": ["A", "A", "B", "C", "B"],
}
)
print(df)
# 结果如下:
shape: (5, 4)
┌──────┬───────┬──────────┬────────┐
│ nrs ┆ names ┆ random ┆ groups │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str │
╞══════╪═══════╪══════════╪════════╡
│ 1 ┆ foo ┆ 0.154163 ┆ A │
│ 2 ┆ ham ┆ 0.74005 ┆ A │
│ 3 ┆ spam ┆ 0.263315 ┆ B │
│ null ┆ egg ┆ 0.533739 ┆ C │
│ 5 ┆ spam ┆ 0.014575 ┆ B │
└──────┴───────┴──────────┴────────┘
字符串
以下部分讨论对
String
数据执行的操作,这是
DataType
在使用
DataFrames
.但是,由于字符串的内存大小不可预测,处理字符串通常效率低下,导致 CPU 访问许多随机内存位置。为了解决这个问题,Polars 使用 Arrow 作为其后端,将所有字符串存储在一个连续的内存块中。因此,字符串遍历对于 CPU 来说是缓存最优且可预测的。
可以通过具有
String
数据类型的列的
.str
属性访问
str
命名空间。在以下示例中,我们创建一个名为
animal
的列,并根据字节数和字符数计算列中每个元素的长度。如果您使用的是 ASCII 文本,那么这两个计算的结果将是相同的,建议使用
lengths
,因为它更快。
df = pl.DataFrame({"animal": ["Crab", "cat and dog", "rab$bit", None]})
out = df.select(
pl.col("animal").str.len_bytes().alias("byte_count"),
pl.col("animal").str.len_chars().alias("letter_count"),
)
print(out)
# 结果如下:
shape: (4, 2)
┌────────────┬──────────────┐
│ byte_count ┆ letter_count │
│ --- ┆ --- │
│ u32 ┆ u32 │
╞════════════╪══════════════╡
│ 4 ┆ 4 │
│ 11 ┆ 11 │
│ 7 ┆ 7 │
│ null ┆ null │
└────────────┴──────────────┘
out = df.select(
pl.col("animal"),
pl.col("animal").str.contains("cat|bit").alias("regex"),
pl.col("animal").str.contains("rab$", literal=True).alias("literal"),
pl.col("animal").str.starts_with("rab").alias("starts_with"),
pl.col("animal").str.ends_with("dog").alias("ends_with"),
)
print(out)
# 结果如下:
shape: (4, 5)
┌─────────────┬───────┬─────────┬─────────────┬───────────┐
│ animal ┆ regex ┆ literal ┆ starts_with ┆ ends_with │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ bool ┆ bool ┆ bool ┆ bool │
╞═════════════╪═══════╪═════════╪═════════════╪═══════════╡
│ Crab ┆ false ┆ false ┆ false ┆ false │
│ cat and dog ┆ true ┆ false ┆ false ┆ true │
│ rab$bit ┆ true ┆ true ┆ true ┆ false │
│ null ┆ null ┆ null ┆ null ┆ null │
df = pl.DataFrame(
{
"a": [
"http://vote.com/ballon_dor?candidate=messi&ref=polars",
"http://vote.com/ballon_dor?candidat=jorginho&ref=polars",
"http://vote.com/ballon_dor?candidate=ronaldo&ref=polars",
]
}
)
out = df.select(
pl.col("a").str.extract(r"candidate=(\w+)", group_index=1),
)
print(out)
# 结果如下:
shape: (3, 1)
┌─────────┐
│ a │
│ --- │
│ str │
╞═════════╡
│ messi │
│ null │
│ ronaldo │
└─────────┘
df = pl.DataFrame({"foo": ["123 bla 45 asd", "xyz 678 910t"]})
out = df.select(
pl.col("foo").str.extract_all(r"(\d+)").alias("extracted_nrs"),
)
print(out)
# 结果如下:
shape: (2, 1)
┌────────────────┐
│ extracted_nrs │
│ --- │
│ list[str] │
╞════════════════╡
│ ["123", "45"] │
│ ["678", "910"] │
└────────────────┘
df = pl.DataFrame({"id": [1, 2], "text": ["123abc", "abc456"]})
out = df.with_columns(
pl.col("text").str.replace(r"abc\b", "ABC"),
pl.col("text").str.replace_all("a", "-", literal=True).alias("text_replace_all"),
)
print(out)
# 结果如下:
shape: (2, 3)
┌─────┬────────┬──────────────────┐
│ id ┆ text ┆ text_replace_all │
│ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str │
╞═════╪════════╪══════════════════╡
│ 1 ┆ 123ABC ┆ 123-bc │
│ 2 ┆ abc456 ┆ -bc456 │
└─────┴────────┴──────────────────┘
缺失值
(
DataFrame
或等效的 )
Series
中的每一列都是一个 Arrow 数组或基于 Apache Arrow 规范的 Arrow 数组的集合。缺失数据在箭头和极坐标中表示,并带有一个
null
值。此
null
缺失值适用于所有数据类型,包括数值。