Минимальные периоды скользящего среднего

Скажем, у меня есть:

data = {
    'id': ['a', 'a', 'a', 'b', 'b', 'b', 'b'],
    'd': [1,2,3,0,1,2,3],
    'sales': [5,1,3,4,1,2,3],
}

Я хотел бы добавить столбец со скользящим средним с размером окна 2, с min_periods=2, более 'id'

В Polars я могу:

import polars as pl

df = pl.DataFrame(data)
df.with_columns(sales_rolling = pl.col('sales').rolling_mean(2).over('id'))
shape: (7, 4)
┌─────┬─────┬───────┬───────────────┐
│ id  ┆ d   ┆ sales ┆ sales_rolling │
│ --- ┆ --- ┆ ---   ┆ ---           │
│ str ┆ i64 ┆ i64   ┆ f64           │
╞═════╪═════╪═══════╪═══════════════╡
│ a   ┆ 1   ┆ 5     ┆ null          │
│ a   ┆ 2   ┆ 1     ┆ 3.0           │
│ a   ┆ 3   ┆ 3     ┆ 2.0           │
│ b   ┆ 0   ┆ 4     ┆ null          │
│ b   ┆ 1   ┆ 1     ┆ 2.5           │
│ b   ┆ 2   ┆ 2     ┆ 1.5           │
│ b   ┆ 3   ┆ 3     ┆ 2.5           │
└─────┴─────┴───────┴───────────────┘

Что такое эквивалент DuckDB? я пробовал

import duckdb

duckdb.sql("""
    select
        *,
        mean(sales) over (
            partition by id 
            order by d
            range between 1 preceding and 0 following
        ) as sales_rolling 
    from df
""").sort('id', 'd')

но получить

┌─────────┬───────┬───────┬───────────────┐
│   id    │   d   │ sales │ sales_rolling │
│ varchar │ int64 │ int64 │    double     │
├─────────┼───────┼───────┼───────────────┤
│ a       │     1 │     5 │           5.0 │
│ a       │     2 │     1 │           3.0 │
│ a       │     3 │     3 │           2.0 │
│ b       │     0 │     4 │           4.0 │
│ b       │     1 │     1 │           2.5 │
│ b       │     2 │     2 │           1.5 │
│ b       │     3 │     3 │           2.5 │
└─────────┴───────┴───────┴───────────────┘

Это очень близко, но Duckdb по-прежнему вычисляет скользящее среднее, даже если в окне имеется только одно значение. Как я могу воспроизвести поведение min_periods=2 (по умолчанию) из Polars?

🤔 А знаете ли вы, что...
В Python есть среды разработки, такие как Jupyter Notebook, которые упрощают работу с данными и исследованиями.


4
50
1

Ответ:

Решено

Вы можете использовать оператор падеж и счетчик:

duckdb.sql("""
    from df
    select
        *,
        case
            when count(*) over rolling2 = 2 then 
                mean(sales) over rolling2
        end as sales_rolling
    window rolling2 as (
        partition by id 
        order by d
        rows between 1 preceding and current row
    )   
""").sort('id', 'd')

┌─────────┬───────┬───────┬───────────────┐
│   id    │   d   │ sales │ sales_rolling │
│ varchar │ int64 │ int64 │    double     │
├─────────┼───────┼───────┼───────────────┤
│ a       │     1 │     5 │          NULL │
│ a       │     2 │     1 │           3.0 │
│ a       │     3 │     3 │           2.0 │
│ b       │     0 │     4 │          NULL │
│ b       │     1 │     1 │           2.5 │
│ b       │     2 │     2 │           1.5 │
│ b       │     3 │     3 │           2.5 │
└─────────┴───────┴───────┴───────────────┘

Обратите внимание, что здесь я использовал именованное окно и обрамление строк.