Polars DataFrame在Python中使用mask替换,mask也是一个Polars DataFrame

1 投票
2 回答
74 浏览
提问于 2025-04-13 18:35

如何用另一个布尔掩码来改变变量(或重新创建)数据框(DataFrame)?这里不是说单独的列向量(Series),而是两个数据框。

比如说,把所有金额大于270的地方都设置成1000,最后的值就会变成1000。

输入:

            apples[0].amount  apples[1].amount...  apples[3].amount  apples[4].amount
    0                  NaN       321.68012  ...             NaN             NaN
    1                  NaN             NaN  ...             NaN       259.70487
    2                  NaN             NaN  ...             NaN       259.70487
    3                  NaN             NaN  ...             NaN       259.70487
    4                  NaN             NaN  ...             NaN       259.70487
    ...                ...             ...  ...             ...             ...
    440582        79.57273             NaN  ...             NaN             NaN
    440583             NaN             NaN  ...             NaN             NaN
    440584             NaN             NaN  ...             NaN             NaN
    440585             NaN             NaN  ...             NaN             NaN
    440586             NaN             NaN  ...       299.91544             NaN


    [440587 rows x 5 columns]

期望的输出:

            apples[0].amount  apples[1].amount...  apples[3].amount  apples[4].amount
    0                  NaN       1000.00000 ...             NaN             NaN
    1                  NaN             NaN  ...             NaN       259.70487
    2                  NaN             NaN  ...             NaN       259.70487
    3                  NaN             NaN  ...             NaN       259.70487
    4                  NaN             NaN  ...             NaN       259.70487
    ...                ...             ...  ...             ...             ...
    440582        79.57273             NaN  ...             NaN             NaN
    440583             NaN             NaN  ...             NaN             NaN
    440584             NaN             NaN  ...             NaN             NaN
    440585             NaN             NaN  ...             NaN             NaN
    440586             NaN             NaN  ...       1000.00000            NaN


    [440587 rows x 5 columns]

另一个例子: cum_sum_volume_apples 的输入:

        apples[0].amount  apples[1].amount  ...  apples[3].amount  apples[4].amount
0            321.66164      1322.18012  ...      1581.98712      1683.34388
1            321.66164       574.39164  ...       849.15207      1260.20487
2            321.66164       574.39164  ...       849.15207      1260.20487
3            321.66164       574.39164  ...       849.15207      1260.20487
4            321.66164       574.39164  ...       849.15207      1260.20487
...                ...             ...  ...             ...             ...
440582      1080.07273      1089.38273  ...      3248.32543      3266.94847
440583         9.06278        26.69990  ...      1107.99783      1117.30783
440584       346.34516       363.98228  ...      1445.28021      1454.59021
440585       346.34516       363.98228  ...       882.09418       891.40418
440586       426.89556       773.24072  ...      1300.41544      1308.98974

[440587 rows x 5 columns]

at_or_above_threshold_mask ~1000

        apples[0].amount  apples[1].amount  ...  apples[3].amount  apples[4].amount
0                False            True  ...           False           False
1                False           False  ...           False            True
2                False           False  ...           False            True
3                False           False  ...           False            True
4                False           False  ...           False            True
...                ...             ...  ...             ...             ...
440582            True           False  ...           False           False
440583           False           False  ...           False           False
440584           False           False  ...           False           False
440585           False           False  ...           False           False
440586           False           False  ...            True           False

[440587 rows x 5 columns]

如何仅在另一个具有相同行列长度的数据框上,使用 at_threshold_mask 来过滤出真实值?(一个示例可以是对上面已有的 cum_sum_volume_apples 应用掩码)

cum_sum_all = pl.cum_sum_horizontal("*")
at_or_above_threshold_boolean_cum_sum = (
        (cum_sum_volume_apples >= volume_threshold).select(cum_sum_all).unnest("cum_sum")
    )
at_or_above_threshold_mask = at_or_above_threshold_boolean_cum_sum >= 1
at_threshold_mask = at_or_above_threshold_boolean_cum_sum == 1

2 个回答

2

在这个情况下,“掩码”是来自于数据框本身,这时你可以使用 pl.when().then()

我们可以使用 pl.all() 来一次性引用所有的列。

df.with_columns(
   pl.when(pl.all() > 270)
     .then(1000.0)         # replace with 1000.0
     .otherwise(pl.all())  # otherwise keep the existing value
     .name.keep()
)
shape: (10, 4)
┌──────────┬────────┬────────┬───────────┐
│ A        ┆ B      ┆ C      ┆ D         │
│ ---      ┆ ---    ┆ ---    ┆ ---       │
│ f64      ┆ f64    ┆ f64    ┆ f64       │
╞══════════╪════════╪════════╪═══════════╡
│ null     ┆ 1000.0 ┆ null   ┆ null      │
│ null     ┆ null   ┆ null   ┆ 259.70487 │
│ null     ┆ null   ┆ null   ┆ 259.70487 │
│ null     ┆ null   ┆ null   ┆ 259.70487 │
│ null     ┆ null   ┆ null   ┆ 259.70487 │
│ 79.57273 ┆ null   ┆ null   ┆ null      │
│ null     ┆ null   ┆ null   ┆ null      │
│ null     ┆ null   ┆ null   ┆ null      │
│ null     ┆ null   ┆ null   ┆ null      │
│ null     ┆ null   ┆ 1000.0 ┆ null      │
└──────────┴────────┴────────┴───────────┘

如果没有 name.keep(),我们会得到:

ComputeError: the name: 'literal' passed to `LazyFrame.with_columns` is duplicate

这是因为为名为 1000.0 的多个列创建了一个叫 literal 的东西。

>>> pl.select(1000.0) # same as pl.select(pl.lit(1000.0))
shape: (1, 1)
┌─────────┐
│ literal │
│ ---     │
│ f64     │
╞═════════╡
│ 1000.0  │
└─────────┘

.name.keep() 会保留原来的列名(在这个例子中是来自 pl.all() 的列名)。

2

除了@jquirous提供的答案,你还可以使用.clip这个方法来替换掉那些超过或低于某个设定范围的值。

import polars as pl
from polars import col

df = pl.DataFrame({
    'a': [1, 5, 10, 15, 20],
    'b': [1, 2, 3, 20, 10],
    'c': [3, 5, 3, 18, 20]
})

print(
    df,
    # shape: (5, 3)
    # ┌─────┬─────┬─────┐
    # │ a   ┆ b   ┆ c   │
    # │ --- ┆ --- ┆ --- │
    # │ i64 ┆ i64 ┆ i64 │
    # ╞═════╪═════╪═════╡
    # │ 1   ┆ 1   ┆ 3   │
    # │ 5   ┆ 2   ┆ 5   │
    # │ 10  ┆ 3   ┆ 3   │
    # │ 15  ┆ 20  ┆ 18  │
    # │ 20  ┆ 10  ┆ 20  │
    # └─────┴─────┴─────┘

    df.select(pl.all().clip(upper_bound=15)),
    # shape: (5, 3)
    # ┌─────┬─────┬─────┐
    # │ a   ┆ b   ┆ c   │
    # │ --- ┆ --- ┆ --- │
    # │ i64 ┆ i64 ┆ i64 │
    # ╞═════╪═════╪═════╡
    # │ 1   ┆ 1   ┆ 3   │
    # │ 5   ┆ 2   ┆ 5   │
    # │ 10  ┆ 3   ┆ 3   │
    # │ 15  ┆ 15  ┆ 15  │
    # │ 15  ┆ 10  ┆ 15  │
    # └─────┴─────┴─────┘

    sep='\n\n'
)

撰写回答