专栏名称: 学姐带你玩AI
这里有人工智能前沿信息、算法技术交流、机器学习/深度学习经验分享、AI大赛解析、大厂大咖算法面试分享、人工智能论文技巧、AI环境工具库教程等……学姐带你玩转AI!
目录
相关文章推荐
重庆校园频道  ·  2025年全国中小学生英语作文征集活动正式启 ... ·  13 小时前  
重庆校园频道  ·  2025年全国中小学生英语作文征集活动正式启 ... ·  13 小时前  
吉林教育  ·  吉林省春季开学“家长第一课”举办 ·  昨天  
江苏警方  ·  关于2027年高考,江苏省教育厅最新公告! ·  2 天前  
东莞本地宝  ·  2025四六级查分时间+成绩查询入口! ·  2 天前  
51好读  ›  专栏  ›  学姐带你玩AI

提升效率,必看这个Python数据处理神器!

学姐带你玩AI  · 公众号  ·  · 2024-07-17 18:29

正文

来源:投稿  作者: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 库,该库:

  • 利用计算机上所有可用的内核。
  • 优化查询以减少不必要的工作/内存分配。
  • 处理比可用 RAM 大得多的数据集。
  • 一致且可预测的 API。
  • 遵循严格的架构(在运行查询之前应知道数据类型)。

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": [123],
        "date": [
            datetime(202511),
            datetime(202512),
            datetime(2025 13),
        ],
        "float": [4.05.06.0],
        "string": ["a""b""c"],
    }
)

print(df)

# 结果如下:
shape: (34)
┌─────────┬─────────────────────┬───────┬────────┐
│ 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: (34)
┌─────────┬────────────────────────────┬───────┬────────┐
│ 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
  • filter
  • with_columns
  • group_by

Select选择

要选择列,我们需要做两件事:

  • 定义 DataFrame 我们想要的数据来源。
  • 选择我们需要的数据。

在下面的示例中,您会看到我们选择 col('*') 。星号代表所有列。

df.select(pl.col("*"))

# 结果如下:
shape: (54)
┌─────┬──────────┬─────────────────────┬───────┐
│ 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: (52)
┌─────┬──────────┐
│ 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(2025122), datetime(2025123)),
)

# 结果如下:
shape: (24)
┌─────┬──────────┬─────────────────────┬─────┐
│ 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: (34)
┌─────┬──────────┬─────────────────────┬───────┐
│ 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: (56)
┌─────┬──────────┬─────────────────────┬───────┬──────────┬───────────┐
│ 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: (82)
┌─────┬─────┐
│ 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: (42)
┌─────┬─────┐
│ 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: (43)
┌─────┬───────┬─────┐
│ 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: (53)
┌─────┬──────────┬──────────┐
│ 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: (54)
┌─────┬──────────┬─────────────────────┬──────────┐
│ 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": [12.0, float("nan"), float("nan"), 0-5-42None],
    }
)

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: (84)
┌─────┬──────────┬───────┬─────┐
│ 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: (85)
┌─────┬──────────┬───────┬─────┬─────┐
│ 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", [12345])
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": [12345],
        "date": [
            datetime(202211),
            datetime(202212),
            datetime(202213),
            datetime(202214),
            datetime(202215),
        ],
        "float": [4.05.06.07.08.0],
    }
)

print(df)

# 结果如下:
shape: (53)
┌─────────┬─────────────────────┬───────┐
│ 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: (33)
┌─────────┬─────────────────────┬───────┐
│ 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: (33)
┌─────────┬─────────────────────┬───────┐
│ 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: (23)
┌─────────┬─────────────────────┬───────┐
│ 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: (94)
┌────────────┬──────────┬─────────────────────┬──────────┐
│ 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(...)
  • Filtering: df.filter()
  • Group by / Aggregation: df.group_by(...).agg(...)

以下示例在以下 DataFrame 情况下执行:

df = pl.DataFrame(
    {
        "nrs": [123None5],
        "names": ["foo""ham""spam""egg"None],
        "random": np.random.rand(5),
        "groups": ["A""A""B""C""B"],
    }
)
print(df)

# 结果如下:
shape: (54)
┌──────┬───────┬──────────┬────────┐
│ 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: (54)
┌─────┬───────┬────────────┬────────┐
│ 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: (56)
┌──────┬───────┬──────────┬────────┬─────────┬───────┐
│ 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: (26)
┌─────┬───────┬──────────┬────────┬─────────┬───────┐
│ 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: (35)
┌────────┬─────┬───────┬────────────┬────────────────┐
│ 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: (54)
┌─────────┬─────────┬──────────────┬──────────────┐
│ 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: (56)
┌─────────┬──────────────┬──────────┬──────────┬──────────┬─────────┐
│ 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": [942],
        "place": ["Mars""Earth""Saturn"],
        "date": pl.date_range(date(202211), date(202213), "1d", eager=True),
        "sales": [33.42142134.144.7],
        "has_people": [FalseTrueFalse],
        "logged_at": pl.datetime_range(
            datetime(2022121), datetime(2022121002), "1s", eager=True
        ),
    }
).with_row_index("index")
print(df)

# 结果如下:
shape: (37)
┌───────┬─────┬────────┬────────────┬───────────┬────────────┬─────────────────────┐
│ 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": [123None5],
        "names": ["foo""ham""spam""egg""spam"],
        "random": np.random.rand(5),
        "groups": ["A""A""B""C""B"],
    }
)
print(df)

# 结果如下:
shape: (54)
┌──────┬───────┬──────────┬────────┐
│ 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: (42)
┌────────────┬──────────────┐
│ 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: (45)
┌─────────────┬───────┬─────────┬─────────────┬───────────┐
│ 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: (31)
┌─────────┐
│ 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: (21)
┌────────────────┐
│ extracted_nrs  │
│ ---            │
│ list[str]      │
╞════════════════╡
│ ["123""45"]  │
│ ["678""910"] │
└────────────────┘

df = pl.DataFrame({"id": [12], "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: (23)
┌─────┬────────┬──────────────────┐
│ id  ┆ text   ┆ text_replace_all │
│ --- ┆ ---    ┆ ---              │
│ i64 ┆ str    ┆ str              │
╞═════╪════════╪══════════════════╡
│ 1   ┆ 123ABC ┆ 123-bc           │
│ 2   ┆ abc456 ┆ -bc456           │
└─────┴────────┴──────────────────┘

缺失值

DataFrame 或等效的 ) Series 中的每一列都是一个 Arrow 数组或基于 Apache Arrow 规范的 Arrow 数组的集合。缺失数据在箭头和极坐标中表示,并带有一个 null 值。此 null 缺失值适用于所有数据类型,包括数值。







请到「今天看啥」查看全文