如何按索引增加极坐标数据框列的值

How to increase values of polars dataframe column by index

我有一个数据框如下

┌────────────┬──────────┬──────────┬──────────┬──────────┐
│ time       ┆ open     ┆ high     ┆ low      ┆ close    │
│ ---        ┆ ---      ┆ ---      ┆ ---      ┆ ---      │
│ i64        ┆ f64      ┆ f64      ┆ f64      ┆ f64      │
╞════════════╪══════════╪══════════╪══════════╪══════════╡
│ 1649016000 ┆ 46405.49 ┆ 47444.11 ┆ 46248.84 ┆ 46407.35 │
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1649030400 ┆ 46407.36 ┆ 46461.14 ┆ 45744.77 ┆ 46005.44 │
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1649044800 ┆ 46005.43 ┆ 46293.38 ┆ 45834.39 ┆ 46173.99 │
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1649059200 ┆ 46174.0  ┆ 46287.97 ┆ 45787.0  ┆ 46160.09 │
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ ...        ┆ ...      ┆ ...      ┆ ...      ┆ ...      │
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1653278400 ┆ 30171.32 ┆ 30670.51 ┆ 30101.07 ┆ 30457.01 │
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1653292800 ┆ 30457.01 ┆ 30616.18 ┆ 30281.89 ┆ 30397.11 │
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1653307200 ┆ 30397.12 ┆ 30625.98 ┆ 29967.07 ┆ 30373.53 │
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1653321600 ┆ 30373.53 ┆ 30529.9  ┆ 30042.09 ┆ 30121.02 │
└────────────┴──────────┴──────────┴──────────┴──────────┘

我想计算每个价格(最低价和最高价)在 2 到 50 的 window 范围内的局部 minimum/maximum 次数。

首先,我为每行添加两列作为本地 min/max 的计数,并用零填充

raw_data["lmin_count"] = np.zeros(len(raw_data), dtype=np.int16)
raw_data["lmax_count"] = np.zeros(len(raw_data), dtype=np.int16)

然后我迭代 window 长度从 2 到 50 并使用以下方法找到每个本地 min/max 的索引:

for _order in range(2, 51):
    local_minima = argrelextrema(raw_data["low"].to_numpy(), np.less, order=_order)[0]
    local_maxima = argrelextrema(raw_data["high"].to_numpy(), np.greater, order=_order)[0]

其中 order 是 window 长度。

并且在 window 长度的每次迭代中,我想通过在 local_minimalocal_maxima 中找到的索引增加 lmin_countlmax_count 的值 我尝试通过这段代码增加价值:

if len(local_minima) > 1:
    raw_data[local_minima,5] += 1
if len(local_maxima) > 1:
    raw_data[local_minima,6] += 1

其中 local_minimalocal_maxima 是索引数组,56lmin_countlmax_count 列的索引。

但出现错误 not implemented

那么按行索引增加(或分配)值的最佳方法是什么?

更新 2022/05/24

由于答案非常有帮助,现在我遇到了其他问题。 我更改了我的代码如下:

min_expr_list = [
    (
        pl.col("price").rolling_min(
            window_size=_order * 2 + 1, min_periods=_order + 2, center=True
        )
        == pl.col("price")
    ).cast(pl.UInt32)
    for _order in range(200, 1001)
]

max_expr_list = [
    (
        pl.col("price").rolling_max(
            window_size=_order * 2 + 1, min_periods=_order + 2, center=True
        )
        == pl.col("price")
    ).cast(pl.UInt32)
    for _order in range(200, 1001)
]
raw_data = raw_data.with_columns(
    [
        pl.sum(min_expr_list).alias("min_freq"),
        pl.sum(max_expr_list).alias("max_freq"),
    ]
)

第一:是否可以将min_expr_listmax_expr_list合并到一个列表中?如果可能的话,在 with_columns 表达式中,如何根据列表的每个元素添加单独的列?

我面临的另一个问题是这种方法的内存使用情况。 在前面的示例中 _order 是有限的,但实际上它比示例更宽。

目前我有数百万条记录的数据集(其中一些有超过 1000 万条记录)并且 _orders 范围可以从 2 到 1500,因此计算需要大量 GB 的内存。

有更好的方法吗?

还有一个问题。当将 _order 增加到超过 1000 时,它似乎不起作用。源代码有什么限制吗?

您编写了非常命令式的代码,但并不是真正的惯用代码。您通常甚至不应该知道值的索引在哪里。相反,您通过 conditions 分配,例如使用 when(condition) -> then(value) -> otherwise(value) 表达式。

您在 when 中的条件仍然可以参考索引。这个片段例如等于分配给一个特定的索引,但是写得更实用:

pl.DataFrame({
    "letters": ["a", "b", "c", "d"]
}).with_column(
    # use a condition to determine the index location
    pl.when(pl.arange(0, pl.count()) == 2)
      .then("idx_2")
      .otherwise("letters").alias("letters")
)
shape: (4, 1)
┌─────────┐
│ letters │
│ ---     │
│ str     │
╞═════════╡
│ letters │
├╌╌╌╌╌╌╌╌╌┤
│ letters │
├╌╌╌╌╌╌╌╌╌┤
│ idx_2   │
├╌╌╌╌╌╌╌╌╌┤
│ letters │
└─────────┘

你的意图,算本地minima/maxima

为了帮助您解决问题,我想向您展示如何以惯用的极地方式找到局部最小值。

本地 minima/maxima 可以通过以下方式找到:

  • 取函数 x 的导数 dy/dx
  • 计算该导数的 sign 可以告诉我们函数斜率在哪里增加和减少。
  • 如果我们对 dy/dx 的符号求导,我们知道符号在哪里变化,因此我们知道局部 minima/maxima.

让我们在假人身上试试这个 DataFrame

df = pl.DataFrame({
    "x": [8, 4, 2, 7, 9, 6, 3, 0]
})

# find the local minima/maxima
df = df.with_columns([
    (pl.col("x").diff().sign().diff().shift(-1) == -2).alias("local_maximum"),
    (pl.col("x").diff().sign().diff().shift(-1) == 2).alias("local_minimum")
])
print(df)
shape: (8, 3)
┌─────┬───────────────┬───────────────┐
│ x   ┆ local_maximum ┆ local_minimum │
│ --- ┆ ---           ┆ ---           │
│ i64 ┆ bool          ┆ bool          │
╞═════╪═══════════════╪═══════════════╡
│ 8   ┆ false         ┆ false         │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 4   ┆ false         ┆ false         │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2   ┆ false         ┆ true          │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 7   ┆ false         ┆ false         │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 9   ┆ true          ┆ false         │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 6   ┆ false         ┆ false         │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3   ┆ false         ┆ false         │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 0   ┆ false         ┆ false         │
└─────┴───────────────┴───────────────┘

接下来我们可以取cumulative sum来统计看到的局部最小值和最大值的总数。

df.with_columns([
    pl.col("local_maximum").cumsum().alias("local_max_count"),
    pl.col("local_minimum").cumsum().alias("local_min_count")
])
shape: (8, 5)
┌─────┬───────────────┬───────────────┬─────────────────┬─────────────────┐
│ x   ┆ local_maximum ┆ local_minimum ┆ local_max_count ┆ local_min_count │
│ --- ┆ ---           ┆ ---           ┆ ---             ┆ ---             │
│ i64 ┆ bool          ┆ bool          ┆ u32             ┆ u32             │
╞═════╪═══════════════╪═══════════════╪═════════════════╪═════════════════╡
│ 8   ┆ false         ┆ false         ┆ 0               ┆ 0               │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 4   ┆ false         ┆ false         ┆ 0               ┆ 0               │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2   ┆ false         ┆ true          ┆ 0               ┆ 1               │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 7   ┆ false         ┆ false         ┆ 0               ┆ 1               │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 9   ┆ true          ┆ false         ┆ 1               ┆ 1               │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 6   ┆ false         ┆ false         ┆ 1               ┆ 1               │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3   ┆ false         ┆ false         ┆ 1               ┆ 1               │
├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 0   ┆ false         ┆ false         ┆ 1               ┆ 1               │
└─────┴───────────────┴───────────────┴─────────────────┴─────────────────┘

我希望这有助于推动您朝着正确的方向前进。

让我看看我们是否可以在@ritchie46 的回复基础上进一步推动您接近终点线。

数据

我在您的示例数据中连接了 'open'、'high' 和 'low' 列,只是为了给我们提供一些数据供我们使用。我还添加了一个 row_nr 专栏,仅供讨论。 (它不会用于任何计算,因此您无需将其包含在最终代码中。)

import numpy as np
import polars as pl
from scipy.signal import argrelextrema

df = pl.DataFrame(
    {
        "col1": [
            46405.49, 46407.36, 46005.43, 46174.00, 30171.32, 30457.01, 30397.12, 30373.53,
            47444.11, 46461.14, 46293.38, 46287.97, 30670.51, 30616.18, 30625.98, 30529.90,
            46248.84, 45744.77, 45834.39, 45787.00, 30101.07, 30281.89, 29967.07, 30042.09,
        ]
    }
).with_row_count()
df
shape: (24, 2)
┌────────┬──────────┐
│ row_nr ┆ col1     │
│ ---    ┆ ---      │
│ u32    ┆ f64      │
╞════════╪══════════╡
│ 0      ┆ 46405.49 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1      ┆ 46407.36 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 2      ┆ 46005.43 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 3      ┆ 46174.0  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 4      ┆ 30171.32 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 5      ┆ 30457.01 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 6      ┆ 30397.12 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 7      ┆ 30373.53 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 8      ┆ 47444.11 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 9      ┆ 46461.14 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 10     ┆ 46293.38 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 11     ┆ 46287.97 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 12     ┆ 30670.51 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 13     ┆ 30616.18 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 14     ┆ 30625.98 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 15     ┆ 30529.9  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 16     ┆ 46248.84 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 17     ┆ 45744.77 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 18     ┆ 45834.39 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 19     ┆ 45787.0  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 20     ┆ 30101.07 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 21     ┆ 30281.89 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 22     ┆ 29967.07 │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 23     ┆ 30042.09 │
└────────┴──────────┘

现在,让我们运行对这些数据进行 scipy.signal.argrelextrema 编码。

for _order in range(1, 7):
    print(
        "order:", _order, ":", argrelextrema(df["col1"].to_numpy(), np.less, order=_order)
    )
order: 1 : (array([ 2,  4,  7, 13, 15, 17, 20, 22]),)
order: 2 : (array([ 4,  7, 15, 22]),)
order: 3 : (array([ 4, 15, 22]),)
order: 4 : (array([ 4, 15, 22]),)
order: 5 : (array([ 4, 22]),)
order: 6 : (array([ 4, 22]),)

从输出来看,您似乎在尝试找到任何行的 index,它是以该行为中心的 window 的最小值,各种 window 尺寸。

例如,row_nr 2 是大小为 3 的 window 的局部最小值,以 row_nr 2 为中心。(这里,order=1 在对argrelextrema 表示“包括上下各一个值”,因此“window size” = (order * 2) + 1) = 3.

让我们在 Polars 中复制这个。我们会逐步采取行动。

rolling_min

首先,让我们使用 rolling_min 表达式来计算从 1 到 6 对应于 order 的滚动最小值。请注意,Polars 允许我们在 [=38= 之外生成一个表达式列表] 语境。 (这通常有助于使代码更具可读性。)

我正在将 scipy order 关键字转换为 rolling_min 的等效 window_size。此外,我正在设置 min_periods 以确保在任何 window 的中心值的每一侧至少有 one 值(以复制scipy 计算)。

expr_list = [
        pl.col("col1").rolling_min(
            window_size=_order * 2 + 1,
            min_periods=_order + 2,
            center=True
        ).alias("roll_min" + str(_order))
    for _order in range(1, 7)
]
df.with_columns(expr_list)
shape: (24, 8)
┌────────┬──────────┬───────────┬───────────┬───────────┬───────────┬───────────┬───────────┐
│ row_nr ┆ col1     ┆ roll_min1 ┆ roll_min2 ┆ roll_min3 ┆ roll_min4 ┆ roll_min5 ┆ roll_min6 │
│ ---    ┆ ---      ┆ ---       ┆ ---       ┆ ---       ┆ ---       ┆ ---       ┆ ---       │
│ u32    ┆ f64      ┆ f64       ┆ f64       ┆ f64       ┆ f64       ┆ f64       ┆ f64       │
╞════════╪══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ 0      ┆ 46405.49 ┆ null      ┆ null      ┆ null      ┆ null      ┆ null      ┆ null      │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 1      ┆ 46407.36 ┆ 46005.43  ┆ 46005.43  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 2      ┆ 46005.43 ┆ 46005.43  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 3      ┆ 46174.0  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 4      ┆ 30171.32 ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 5      ┆ 30457.01 ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 6      ┆ 30397.12 ┆ 30373.53  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 7      ┆ 30373.53 ┆ 30373.53  ┆ 30373.53  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 8      ┆ 47444.11 ┆ 30373.53  ┆ 30373.53  ┆ 30373.53  ┆ 30171.32  ┆ 30171.32  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 9      ┆ 46461.14 ┆ 46293.38  ┆ 30373.53  ┆ 30373.53  ┆ 30373.53  ┆ 30171.32  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 10     ┆ 46293.38 ┆ 46287.97  ┆ 30670.51  ┆ 30373.53  ┆ 30373.53  ┆ 30373.53  ┆ 30171.32  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 11     ┆ 46287.97 ┆ 30670.51  ┆ 30616.18  ┆ 30616.18  ┆ 30373.53  ┆ 30373.53  ┆ 30373.53  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 12     ┆ 30670.51 ┆ 30616.18  ┆ 30616.18  ┆ 30529.9   ┆ 30529.9   ┆ 30373.53  ┆ 30373.53  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 13     ┆ 30616.18 ┆ 30616.18  ┆ 30529.9   ┆ 30529.9   ┆ 30529.9   ┆ 30529.9   ┆ 30373.53  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 14     ┆ 30625.98 ┆ 30529.9   ┆ 30529.9   ┆ 30529.9   ┆ 30529.9   ┆ 30529.9   ┆ 30101.07  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 15     ┆ 30529.9  ┆ 30529.9   ┆ 30529.9   ┆ 30529.9   ┆ 30529.9   ┆ 30101.07  ┆ 30101.07  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 16     ┆ 46248.84 ┆ 30529.9   ┆ 30529.9   ┆ 30529.9   ┆ 30101.07  ┆ 30101.07  ┆ 29967.07  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 17     ┆ 45744.77 ┆ 45744.77  ┆ 30529.9   ┆ 30101.07  ┆ 30101.07  ┆ 29967.07  ┆ 29967.07  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 18     ┆ 45834.39 ┆ 45744.77  ┆ 30101.07  ┆ 30101.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 19     ┆ 45787.0  ┆ 30101.07  ┆ 30101.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 20     ┆ 30101.07 ┆ 30101.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 21     ┆ 30281.89 ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 22     ┆ 29967.07 ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  ┆ 29967.07  │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 23     ┆ 30042.09 ┆ null      ┆ null      ┆ null      ┆ null      ┆ null      ┆ null      │
└────────┴──────────┴───────────┴───────────┴───────────┴───────────┴───────────┴───────────┘

查看 roll_min_1 中的输出(相当于 order=1 调用 argrelextrema),我们看到 roll_min_1 中的值等于 col1 for row_nr 2, 4, 7, 13, 15, 17, 20, 22 ... 与 argrelextrema for order=1 的输出完全对应。同样,对于其他 roll_min_X 列。我们将在下一步中使用这个事实。

获取行索引

正如@ritchie46 指出的那样,在 Polars 中,我们使用 conditions(不是索引)。我们将修改上面的代码以确定 col1 中的值是否等于它的滚动最小值,对于我们的每个 window 尺寸。

expr_list = [
    (
        pl.col("col1").rolling_min(
            window_size=_order * 2 + 1,
            min_periods=_order + 2,
            center=True
        )
        == pl.col("col1")
    ).alias("min_idx_" + str(_order))
    for _order in range(1, 7)
]
df.with_columns(expr_list)
shape: (24, 8)
┌────────┬──────────┬───────────┬───────────┬───────────┬───────────┬───────────┬───────────┐
│ row_nr ┆ col1     ┆ min_idx_1 ┆ min_idx_2 ┆ min_idx_3 ┆ min_idx_4 ┆ min_idx_5 ┆ min_idx_6 │
│ ---    ┆ ---      ┆ ---       ┆ ---       ┆ ---       ┆ ---       ┆ ---       ┆ ---       │
│ u32    ┆ f64      ┆ bool      ┆ bool      ┆ bool      ┆ bool      ┆ bool      ┆ bool      │
╞════════╪══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ 0      ┆ 46405.49 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 1      ┆ 46407.36 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 2      ┆ 46005.43 ┆ true      ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 3      ┆ 46174.0  ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 4      ┆ 30171.32 ┆ true      ┆ true      ┆ true      ┆ true      ┆ true      ┆ true      │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 5      ┆ 30457.01 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 6      ┆ 30397.12 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 7      ┆ 30373.53 ┆ true      ┆ true      ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 8      ┆ 47444.11 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 9      ┆ 46461.14 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 10     ┆ 46293.38 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 11     ┆ 46287.97 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 12     ┆ 30670.51 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 13     ┆ 30616.18 ┆ true      ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 14     ┆ 30625.98 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 15     ┆ 30529.9  ┆ true      ┆ true      ┆ true      ┆ true      ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 16     ┆ 46248.84 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 17     ┆ 45744.77 ┆ true      ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 18     ┆ 45834.39 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 19     ┆ 45787.0  ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 20     ┆ 30101.07 ┆ true      ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 21     ┆ 30281.89 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 22     ┆ 29967.07 ┆ true      ┆ true      ┆ true      ┆ true      ┆ true      ┆ true      │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤
│ 23     ┆ 30042.09 ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     ┆ false     │
└────────┴──────────┴───────────┴───────────┴───────────┴───────────┴───────────┴───────────┘

请注意,对于 min_idx_1row_nr 2, 4, 7, 13, 15, 17, 20, 22 的值为真,对应于 [=34= 的输出] 对于 order=1。同样,对于其他列。

求和

我们现在可以使用 cast function and the polars.sum 函数对我们的列求和 row-wise。 (事实上​​ ,我们不会保留我们的滚动最小列 - 我们只会保留总和)。

expr_list = [
    (
        pl.col("col1").rolling_min(
            window_size=_order * 2 + 1,
            min_periods=_order + 2,
            center=True
        )
        == pl.col("col1")
    ).cast(pl.UInt32)
    for _order in range(1, 7)
]
df.with_columns(pl.sum(expr_list).alias("min_freq"))
┌────────┬──────────┬──────────┐
│ row_nr ┆ col1     ┆ min_freq │
│ ---    ┆ ---      ┆ ---      │
│ u32    ┆ f64      ┆ u32      │
╞════════╪══════════╪══════════╡
│ 0      ┆ 46405.49 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1      ┆ 46407.36 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 2      ┆ 46005.43 ┆ 1        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 3      ┆ 46174.0  ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 4      ┆ 30171.32 ┆ 6        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 5      ┆ 30457.01 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 6      ┆ 30397.12 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 7      ┆ 30373.53 ┆ 2        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 8      ┆ 47444.11 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 9      ┆ 46461.14 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 10     ┆ 46293.38 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 11     ┆ 46287.97 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 12     ┆ 30670.51 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 13     ┆ 30616.18 ┆ 1        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 14     ┆ 30625.98 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 15     ┆ 30529.9  ┆ 4        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 16     ┆ 46248.84 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 17     ┆ 45744.77 ┆ 1        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 18     ┆ 45834.39 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 19     ┆ 45787.0  ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 20     ┆ 30101.07 ┆ 1        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 21     ┆ 30281.89 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 22     ┆ 29967.07 ┆ 6        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 23     ┆ 30042.09 ┆ 0        │
└────────┴──────────┴──────────┘

我相信这就是您想要获得的结果。

从这里开始,我认为您可以扩展上面的滚动最大值代码。

关系

此代码与 argrelextrema 代码之间的一个区别与平局有关。如果两个值与任何 window 中的最小值并列,argrelextrema 认为 两者都不是 window 的最小值。上面的代码认为 both 是最小值。

我不确定这对于您拥有的 windows 大小或数据类型的可能性有多大。

请将 Polars 更新为 0.13.38

Polars 的最新版本包含对滚动功能性能的一些主要 改进。 (公告在此 Twitter thread。)您将希望通过更新到最新版本来利用它。

更新 - 2022/05/24

将所有列表合并为一个表达式

first: is it possible to merge both min_expr_list and max_expr_list into one list? and if it is possible, in with_columns expression how can I add separate columns based on each element of list?

可以在单个 with_columns 上下文中使用单个列表生成所有列(最小值和最大值,每个顺序,每个变量)。计算是独立的,因此可以在相同的 with_columns 上下文中。每列都有一个名称,可以在以后的计算步骤中使用。

但在那种情况下,累加步骤需要在单独的 with_columns 表达式中。 with_columns 上下文假定所有计算都是独立的 - 并且它们可以 运行 以任何顺序进行,没有依赖性。但是汇总列(通过按名称选择它们)取决于首先创建的那些列。

如果您 return 将它们作为 struct 的系列(例如,使用 map),则可以从单个函数 return 多个系列......但这通常是要避免。 (这超出了我们这里的问题范围。)

更具体地说,对于这个问题,我们正在处理内存压力问题。因此,在这种情况下,我们需要朝相反的方向移动 - 我们需要将列表分成更小的部分并将它们分批提供给 with_columns 表达式。

通过order

批处理计算

currently I have datasets with millions of records (some of them have more than 10 million records) and _orders range can be from 2 to 1500 so calculating needs lots of GB of ram. is there any better way to do that?

我们将尝试一些技术来减少内存压力......并且仍然获得良好的性能。

一种技术是使用fold 方法来累加值。这允许我们对布尔值求和,而不必将每个中间列都转换为整数。这应该在中间计算期间减少内存压力。

我们还将通过将表达式列表分解为 sub-lists、计算中间结果并使用 fold 方法累加到累加器列来对计算进行批处理。

首先,让我们将 min_expr_list 中的 cast 消除为整数。

min_expr_list = [
    (
        pl.col("price").rolling_min(
            window_size=_order * 2 + 1, min_periods=_order + 2, center=True
        )
        == pl.col("price")
    )
    for _order in range(1, 20)
]

接下来我们需要选择一个 batch_size 并初始化一个累加器列。我会尝试不同的 batch_size 数字,直到您找到一个似乎适合您的计算平台和数据集大小的数字。由于我们在这个例子中的数据有限,我会选择一个 batch_size 5 - 只是为了演示算法。

batch_size = 5
df = df.with_column(pl.lit(0, dtype=pl.UInt32).alias("min_freq"))

接下来,我们将遍历 sub-lists 的批次,并不断积累。

while(min_expr_list):
    next_batch, min_expr_list = min_expr_list[0: batch_size], min_expr_list[batch_size:]
    df=(
        df
        .with_column(
            pl.fold(
                pl.col("min_freq"),
                lambda acc, x: acc + x,
                next_batch,
            )
        )
    )

print(df)
shape: (24, 3)
┌────────┬──────────┬──────────┐
│ row_nr ┆ price    ┆ min_freq │
│ ---    ┆ ---      ┆ ---      │
│ u32    ┆ f64      ┆ u32      │
╞════════╪══════════╪══════════╡
│ 0      ┆ 46405.49 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 1      ┆ 46407.36 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 2      ┆ 46005.43 ┆ 1        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 3      ┆ 46174.0  ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 4      ┆ 30171.32 ┆ 15       │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 5      ┆ 30457.01 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 6      ┆ 30397.12 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 7      ┆ 30373.53 ┆ 2        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 8      ┆ 47444.11 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 9      ┆ 46461.14 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 10     ┆ 46293.38 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 11     ┆ 46287.97 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 12     ┆ 30670.51 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 13     ┆ 30616.18 ┆ 1        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 14     ┆ 30625.98 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 15     ┆ 30529.9  ┆ 4        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 16     ┆ 46248.84 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 17     ┆ 45744.77 ┆ 1        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 18     ┆ 45834.39 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 19     ┆ 45787.0  ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 20     ┆ 30101.07 ┆ 1        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 21     ┆ 30281.89 ┆ 0        │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 22     ┆ 29967.07 ┆ 19       │
├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 23     ┆ 30042.09 ┆ 0        │
└────────┴──────────┴──────────┘

order 为 1,000 或更多时 rolling_min 出现问题

and one more side problem. when increasing _order to more than 1000 it seems it doesn't work. is there any limitation in source code?

我生成了 5000 万个随机数的数据集并测试了 rolling_min 订单大小为 1,500 .. 没有发现任何问题。事实上,我使用滚动 slice 复制了算法,没有发现任何错误。

但我有预感可能会发生什么。让我们从这个包含 10 条记录的数据集开始:

df = pl.DataFrame(
    {
        "col1": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    }
)
df
shape: (10, 1)
┌──────┐
│ col1 │
│ ---  │
│ i64  │
╞══════╡
│ 1    │
├╌╌╌╌╌╌┤
│ 2    │
├╌╌╌╌╌╌┤
│ 3    │
├╌╌╌╌╌╌┤
│ 4    │
├╌╌╌╌╌╌┤
│ 5    │
├╌╌╌╌╌╌┤
│ 6    │
├╌╌╌╌╌╌┤
│ 7    │
├╌╌╌╌╌╌┤
│ 8    │
├╌╌╌╌╌╌┤
│ 9    │
├╌╌╌╌╌╌┤
│ 10   │
└──────┘

如果我们设置 _order = 8 和 运行 算法,我们会得到一个结果。

_order = 8
df = df.with_column(
    pl.col("col1")
    .rolling_min(window_size=(2 * _order) + 1, min_periods=(_order + 2), center=True)
    .alias("rolling_min")
)
df
shape: (10, 2)
┌──────┬─────────────┐
│ col1 ┆ rolling_min │
│ ---  ┆ ---         │
│ i64  ┆ i64         │
╞══════╪═════════════╡
│ 1    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2    ┆ 1           │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3    ┆ 1           │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 4    ┆ 1           │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 5    ┆ 1           │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 6    ┆ 1           │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 7    ┆ 1           │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 8    ┆ 1           │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 9    ┆ 1           │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 10   ┆ null        │
└──────┴─────────────┘

然而,如果我们设置_order=9,我们得到所有空值:

shape: (10, 2)
┌──────┬─────────────┐
│ col1 ┆ rolling_min │
│ ---  ┆ ---         │
│ i64  ┆ i64         │
╞══════╪═════════════╡
│ 1    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 4    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 5    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 6    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 7    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 8    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 9    ┆ null        │
├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 10   ┆ null        │
└──────┴─────────────┘

这是你看到的吗? null 值的原因是 min_period 值。我们设置 min_period = _order + 2,在本例中为 11。但是,数据集中只有 10 个值。因此,我们得到所有 null 值。

也许这就是您的数据中发生的情况?