python - 如何更好地使用 Polars 中的 apply?

标签 python python-polars

我有一个极坐标数据框,如下所示。

import polars as pl

df = pl.DataFrame(
    {
        "a": [1, 4, 3, 2, 8, 4, 5, 6],
        "b": [2, 3, 1, 3, 9, 7, 6, 8],
        "c": [1, 1, 1, 1, 2, 2, 2, 2],
    }
)

我的任务是

  1. 按列“c”分组
  2. 对于每个组,检查“a”列中的所有数字是否都小于“b”列中的相应值。
    • 如果是这样,只需在 groupby 上下文中返回与“a”相同的列。
    • 否则,应用一个名为“convert”的第三方函数,它接受两个 numpy 数组并返回一个具有相同大小的 numpy 数组,所以在我的例子中,我可以首先将列“a "和 "b"到 numpy 数组,并将它们作为输入提供给 "convert"。最后,在 groupby 上下文中返回从 "convert" 返回的数组(可能需要在返回之前将其转换为极坐标系)。

因此,对于上面的示例,我想要的输出如下(为了更好地说明,在 groupby 之后进行了分解)。

shape: (8, 2)
┌─────┬─────┐
│ c   ┆ a   │
│ --- ┆ --- │
│ i64 ┆ i64 │
╞═════╪═════╡
│ 1   ┆ 1   │
│ 1   ┆ 3   │
│ 1   ┆ 1   │
│ 1   ┆ 2   │
│ 2   ┆ 8   │
│ 2   ┆ 4   │
│ 2   ┆ 5   │
│ 2   ┆ 6   │
└─────┴─────┘

根据假设,

>>> import numpy as np
>>> convert(np.array([1, 4, 3, 2]), np.array([2, 3, 1, 3]))
np.array([1, 3, 1, 2])

# [1, 4, 3, 2] is from column a of df when column c is 1, and [2, 3, 1, 3] comes from column b of df when column c is 1.
# I have to apply my custom python function 'convert' for the c == 1 group, because not all values in a are smaller than those in b according to the task description above.

我的问题是我应该如何以高性能或极性惯用的方式实现这个逻辑,而不牺牲运行 Rust 代码和并行化所获得的如此多的速度?

我之所以问这个问题,是因为根据我的理解,将 apply 与自定义 python 函数一起使用会减慢程序速度,但就我而言,在某些情况下,我不需要求助于第三方函数来帮助。那么,有什么办法可以让我获得最好的世界呢? (对于不需要第三方功能的场景,充分利用 Polars 的优势,仅在必要时应用第三方功能)。

最佳答案

听起来您想找到匹配的组:

(
   df
   .with_row_count()
   .filter(
      (pl.col("a") >= pl.col("b"))
      .any()
      .over("c"))
)
shape: (4, 4)
┌────────┬─────┬─────┬─────┐
│ row_nr | a   | b   | c   │
│ ---    | --- | --- | --- │
│ u32    | i64 | i64 | i64 │
╞════════╪═════╪═════╪═════╡
│ 0      | 1   | 2   | 1   │
│ 1      | 4   | 3   | 1   │
│ 2      | 3   | 1   | 1   │
│ 3      | 2   | 3   | 1   │
└────────┴─────┴─────┴─────┘

并将您的自定义函数应用于每个组。

(      
   df
   .with_row_count()
   .filter(
      (pl.col("a") >= pl.col("b"))
      .any()
      .over("c"))
   .select(
      pl.col("row_nr"),
      pl.apply(
         ["a", "b"], # np.minimum is just for example purposes
         lambda s: np.minimum(s[0], s[1]))
      .over("c"))
)
shape: (4, 2)
┌────────┬─────┐
│ row_nr | a   │
│ ---    | --- │
│ u32    | i64 │
╞════════╪═════╡
│ 0      | 1   │
│ 1      | 3   │
│ 2      | 1   │
│ 3      | 2   │
└────────┴─────┘

(注意:How to Write Poisson CDF as Python Polars Expression 中可能有一些关于 scipy/numpy ufunc 的有用信息,并可能避免使用 .apply())

然后您可以.join()将结果返回到原始数据。

(
   df
   .with_row_count()
   .join(
      df
      .with_row_count()
      .filter(
         (pl.col("a") >= pl.col("b"))
         .any()
         .over("c"))
      .select(
         pl.col("row_nr"),
         pl.apply(
            ["a", "b"],
            lambda s: np.minimum(s[0], s[1]))
         .over("c")),
      on="row_nr",
      how="left")
)
shape: (8, 5)
┌────────┬─────┬─────┬─────┬─────────┐
│ row_nr | a   | b   | c   | a_right │
│ ---    | --- | --- | --- | ---     │
│ u32    | i64 | i64 | i64 | i64     │
╞════════╪═════╪═════╪═════╪═════════╡
│ 0      | 1   | 2   | 1   | 1       │
│ 1      | 4   | 3   | 1   | 3       │
│ 2      | 3   | 1   | 1   | 1       │
│ 3      | 2   | 3   | 1   | 2       │
│ 4      | 8   | 9   | 2   | null    │
│ 5      | 4   | 7   | 2   | null    │
│ 6      | 5   | 6   | 2   | null    │
│ 7      | 6   | 8   | 2   | null    │
└────────┴─────┴─────┴─────┴─────────┘

然后您可以填写空值。

.with_columns(
   pl.col("a_right").fill_null(pl.col("a")))

关于python - 如何更好地使用 Polars 中的 apply?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75404232/

相关文章:

python - 使用opengl进行平铺渲染

jquery - 使用 Selenium WebDriver Python 上传带有隐藏输入的文件

python - Facebook Messenger 机器人应用程序 - 持久菜单未出现

根据另一列的匹配条件更新 Polars 数据框的一列的 Pythonic 方法

python - 从 Polars 数据框中删除非 ASCII 字符

python - 在 Python Kivy 中查找程序使用的总内存的方法

dataframe - 如何使用 Polars 按值列表过滤 df?

python - 极地 : switching between dtypes within a DataFrame

python - Polars 从日期时间对象中添加/减去 UTC 偏移量

python - matplotlib动画控制各个循环中的间隔