У меня есть набор полярных выражений, используемых для создания функций для модели ML. Я хотел бы добавить в эту коллекцию функцию poission cdf, сохраняя при этом ленивое выполнение (с преимуществами скорости, кэширования и т. д.). Я до сих пор не нашел простой способ добиться этого.
Мне удалось получить результат, который я хотел бы получить за пределами желаемой структуры ленивых выражений, с помощью:
import polars as pl
from scipy.stats import poisson
df = pl.DataFrame({"count": [9,2,3,4,5], "expected_count": [7.7, 0.2, 0.7, 1.1, 7.5]})
result = poisson.cdf(df["count"].to_numpy(), df["expected_count"].to_numpy())
df = df.with_column(pl.Series(result).alias("poission_cdf"))
Однако на самом деле я бы хотел, чтобы это выглядело так:
df = pl.DataFrame({"count": [9,2,3,4,5], "expected_count": [7.7, 0.2, 0.7, 1.1, 7.5]})
df = df.select(
[
... # bunch of other expressions here
poisson_cdf()
]
)
где poisson_cdf - некоторое полярное выражение, например:
def poisson_cdf():
# this is just for illustration, clearly wont work
return scipy.stats.poisson.cdf(pl.col("count"), pl.col("expected_count")).alias("poisson_cdf")
Я также пытался использовать структуру, состоящую из "count" и "expected_count", и применять ее, как рекомендуется в документации, при применении пользовательских функций. Однако на самом деле мой набор данных состоит из нескольких миллионов строк, что приводит к абсурдному времени выполнения.
Любые советы или рекомендации здесь будут оценены. В идеале где-то существует такое выражение? Заранее спасибо!






Похоже, вы хотите использовать .map() вместо .apply(), чтобы сразу передать целые столбцы.
df.select([
pl.all(),
# ...
pl.struct(["count", "expected_count"])
.map(lambda x:
poisson.cdf(x.struct.field("count"), x.struct.field("expected_count")))
.flatten()
.alias("poisson_cdf")
])
shape: (5, 3)
┌───────┬────────────────┬─────────────┐
│ count | expected_count | poisson_cdf │
│ --- | --- | --- │
│ i64 | f64 | f64 │
╞═══════╪════════════════╪═════════════╡
│ 9 | 7.7 | 0.75308 │
│ 2 | 0.2 | 0.998852 │
│ 3 | 0.7 | 0.994247 │
│ 4 | 1.1 | 0.994565 │
│ 5 | 7.5 | 0.241436 │
└───────┴────────────────┴─────────────┘
Если бы scipy.stats.poisson.cdf была реализована как правильная универсальная функция numpy , ее можно было бы использовать непосредственно в полярных выражениях, но это не так. К счастью, CDF Пуассона почти такая же, как регуляризованная верхняя неполная гамма-функция, для которой scipy предоставляет gammaincc, которую можно использовать в выражениях поляр:
>>> import polars as pl
>>> from scipy.special import gammaincc
>>> df = pl.select(pl.arange(0, 10).alias('k'))
>>> df.with_columns(cdf=gammaincc(pl.col('k') + 1, 4.0))
shape: (10, 2)
┌─────┬──────────┐
│ k ┆ cdf │
│ --- ┆ --- │
│ i64 ┆ f64 │
╞═════╪══════════╡
│ 0 ┆ 0.018316 │
│ 1 ┆ 0.091578 │
│ 2 ┆ 0.238103 │
│ 3 ┆ 0.43347 │
│ ... ┆ ... │
│ 6 ┆ 0.889326 │
│ 7 ┆ 0.948866 │
│ 8 ┆ 0.978637 │
│ 9 ┆ 0.991868 │
└─────┴──────────┘
Результат такой же, как и у poisson.cdf:
>>> _.with_columns(cdf2=pl.lit(poisson.cdf(df['k'], 4)))
shape: (10, 3)
┌─────┬──────────┬──────────┐
│ k ┆ cdf ┆ cdf2 │
│ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ f64 │
╞═════╪══════════╪══════════╡
│ 0 ┆ 0.018316 ┆ 0.018316 │
│ 1 ┆ 0.091578 ┆ 0.091578 │
│ 2 ┆ 0.238103 ┆ 0.238103 │
│ 3 ┆ 0.43347 ┆ 0.43347 │
│ ... ┆ ... ┆ ... │
│ 6 ┆ 0.889326 ┆ 0.889326 │
│ 7 ┆ 0.948866 ┆ 0.948866 │
│ 8 ┆ 0.978637 ┆ 0.978637 │
│ 9 ┆ 0.991868 ┆ 0.991868 │
└─────┴──────────┴──────────┘
Вы хотите воспользоваться тем фактом, что scipy имеет набор функций, которые numpy ufuncs как те
по-прежнему имеют быструю работу со столбцами через NumPy API.
В частности, вам нужна функция pdtr.
Затем вы хотите использовать reduce, а не map или apply, поскольку они предназначены для общих функций Python и не будут работать так же хорошо.
Так что если у нас...
df = pl.DataFrame({"count": [9,2,3,4,5], "expected_count": [7.7, 0.2, 0.7, 1.1, 7.5]})
result = poisson.cdf(df["count"].to_numpy(), df["expected_count"].to_numpy())
df = df.with_columns(pl.Series(result).alias("poission_cdf"))
то мы можем добавить к нему с
df=df.with_columns([
pl.reduce(f=pdtr, exprs=[pl.col('count'),pl.col('expected_count')]).alias("poicdf")
])
df
shape: (5, 4)
┌───────┬────────────────┬──────────────┬──────────┐
│ count ┆ expected_count ┆ poission_cdf ┆ poicdf │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ f64 ┆ f64 │
╞═══════╪════════════════╪══════════════╪══════════╡
│ 9 ┆ 7.7 ┆ 0.75308 ┆ 0.75308 │
│ 2 ┆ 0.2 ┆ 0.998852 ┆ 0.998852 │
│ 3 ┆ 0.7 ┆ 0.994247 ┆ 0.994247 │
│ 4 ┆ 1.1 ┆ 0.994565 ┆ 0.994565 │
│ 5 ┆ 7.5 ┆ 0.241436 ┆ 0.241436 │
└───────┴────────────────┴──────────────┴──────────┘
Вы можете видеть, что он дает тот же ответ.
Да это именно то, что я после! С этим конкретным кодом я получаю
InvalidOperationError: explode not supported with dtype Object("object"). Однако, если я просто оберну вызовpoisson.cdfвызовомpl.Seriesи удалюflatten, я попаду туда. ТЫВМ!