我们在Polars的社区上最常遇到的问题之一是如何根据另一个数据框中的值来过滤一个数据框的行。
我认为人们没有意识到这基本上是一个连接操作,因为他们不想要另一个数据框中的任何列。
好消息是,这样的功能已经存在——它被称为:
- 半连接(semi join),如果你想保留也在另一个数据框中找到的行;
- 反连接(anti join),如果你想移除也在另一个数据框中找到的行。
对于一家租车公司,我们有一个包含我们拥有的汽车及其唯一ID的cars数据框……
df_cars = pl.DataFrame(
{
"id": ["a", "b", "c"],
"make": ["ford", "toyota", "bmw"],
})
shape: (3, 2)
┌─────┬────────┐
│ id ┆ make │
│ --- ┆ --- │
│ str ┆ str │
╞═════╪════════╡
│ a ┆ ford │
│ b ┆ toyota │
│ c ┆ bmw │
└─────┴────────┘
以及一个追踪每辆汽车维修记录的“维修”数据框。
df_repairs = pl.DataFrame(
{
"id": ["c", "c"],
"cost": [100, 200],
})
shape: (2, 2)
┌─────┬──────┐
│ id ┆ cost │
│ --- ┆ --- │
│ str ┆ i64 │
╞═════╪══════╡
│ c ┆ 100 │
│ c ┆ 200 │
└─────┴──────┘
我们想要找到:
- 所有已经维修过的汽车(半连接)或者
- 所有没有维修过的汽车(反连接)
df_semi_join = df_cars.join(df_repairs, on="id", how="semi")
shape: (1, 2)
┌─────┬──────┐
│ id ┆ make │
│ --- ┆ --- │
│ str ┆ str │
╞═════╪══════╡
│ c ┆ bmw │
└─────┴──────┘
df_anti_join = df_cars.join(df_repairs, on="id", how="anti")
shape: (2, 2)
┌─────┬────────┐
│ id ┆ make │
│ --- ┆ --- │
│ str ┆ str │
╞═════╪════════╡
│ a ┆ ford │
│ b ┆ toyota │
└─────┴────────┘
请注意,使用这些连接操作,我们最终不会得到来自“维修”数据框的任何列。
半连接和反连接具有标准连接的优点。例如,你可以基于多个列的条件进行这些连接。在Polars中,你可以在Polars的延迟模式下以及对于大数据进行流式处理时执行这些连接。