Предположим, у меня есть функция:
def f(prev, curr):
return prev * 2 + curr
(просто пример, могло быть что угодно)
И фрейм данных Polars:
| some_col | other_col |
|----------|-----------|
| 7 | ...
| 3 |
| 9 |
| 2 |
Я хотел бы использовать f
в своем фрейме данных в совокупности, и результат будет следующим:
| some_col | other_col |
|----------|-----------|
| 7 | ...
| 17 |
| 43 |
| 88 |
Я понимаю, что, естественно, такой тип вычислений не будет очень эффективным, поскольку его придется выполнять по одной строке за раз (по крайней мере, в общем случае).
Очевидно, я могу перебирать строки. Но есть ли элегантный идиоматический способ сделать это в Polars?
Это зависит от того, какую именно операцию вам необходимо выполнить.
Приведенный вами пример можно выразить через .cum_sum()
с дополнительной арифметикой:
def plus_prev_times_2(col):
x = 2 ** pl.int_range(pl.len() - 1).reverse()
y = 2 ** pl.int_range(1, pl.len())
cs = (x * col.slice(1)).cum_sum()
return cs / x + col.first() * y
df = pl.DataFrame({"some_col": [7, 3, 9, 2]})
df.with_columns(
pl.col.some_col.first()
.append(pl.col.some_col.pipe(plus_prev_times_2))
.alias("plus_prev_times_2")
)
shape: (4, 2)
┌──────────┬───────────────────┐
│ some_col ┆ plus_prev_times_2 │
│ --- ┆ --- │
│ i64 ┆ f64 │
╞══════════╪═══════════════════╡
│ 7 ┆ 7.0 │
│ 3 ┆ 17.0 │
│ 9 ┆ 43.0 │
│ 2 ┆ 88.0 │
└──────────┴───────────────────┘
В общем, я считаю, что то, о чем вы просите, называется «Вертикальное сгибание/сканирование».
Polars предлагает только горизонтальную версию, pl.cum_fold
df = pl.DataFrame(dict(a=[7], b=[3], c=[9], d=[2]))
df.with_columns(
pl.cum_fold(acc=0, function=lambda acc, x: acc * 2 + x, exprs=pl.all())
)
shape: (1, 5)
┌─────┬─────┬─────┬─────┬──────────────┐
│ a ┆ b ┆ c ┆ d ┆ cum_fold │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 ┆ i64 ┆ struct[4] │
╞═════╪═════╪═════╪═════╪══════════════╡
│ 7 ┆ 3 ┆ 9 ┆ 2 ┆ {7,17,43,88} │
└─────┴─────┴─────┴─────┴──────────────┘
Как обсуждалось в выпуске, вертикальный эквивалент был бы крайне неэффективен.
Для более эффективного подхода вы можете писать плагины на Rust:
Но использовать что-то вроде numba, вероятно, проще реализовать.
Существует несколько существующих ответов numba, например.
Спасибо! В общем случае (вертикальное сканирование) кажется, что это действительно не поддерживается изначально.