我有一个极坐标数据框:
df = pl.DataFrame(
{
"group": [1, 1, 1, 2, 2, 2, 2],
"mean": [25.5, 25.2, 24.9, 50.5, 55.1, 54.2, 60],
"std": [2.5, 3.5, 1.7, 10.2, 5.5, 7.8, 20.5],
}
)
对于每一行,我们都有正态分布的平均值和标准差。我想添加一列,表示采样时该行在其组中具有最低值的概率。
严格要求该列添加可以输入到
with_columns
调用中的表达式。
我能够写出以下内容:
def probability_smallest_in_group(series):
length = len(series)
means = series.struct.field("mean").to_numpy()
stds = series.struct.field("std").to_numpy()
samples = 100_000
pos = np.argsort(np.argsort(np.random.normal(means, stds, size=(samples, length))))
result = (pos == 0).mean(axis=0)
result = pl.Series(result)
return result
df = df.with_columns(
pl.struct(["mean", "std"])
.apply(probability_smallest_in_group)
.over("group")
.alias("probability_smallest_in_group")
)
我相信这会输出正确的结果(大约)。然而,由于实际数据帧有几百万行,该解决方案是不可行的 - 导致荒谬的执行时间。
解决这个问题的更好方法是什么?有没有不涉及采样的解决方案?
非常感谢任何帮助,所以提前谢谢您!
你绝对可以在不采样的情况下做到这一点,说实话,我认为你的方法没有达到你想要的效果。窗口函数似乎没有批量调用您的自定义函数(要看到这一点,请在顶部添加一个
print(series)
。另外,我不确定让 rng 首先吐出最低值是否是同一件事就说你的房车是最低的。
我对我认为你正在尝试做的事情的回顾:在每个组中,你有很多随机变量,并且你想知道其中每个变量低于该组中所有其他随机变量的概率。
为了确定一个 RV 小于另一个 RV 的概率,您可以这样做this。但是,您不仅仅想要 rv1 < rv2, you want rv1
将其带回极坐标,您需要创建每组中所有 rv 的成对映射,然后获取其乘积。
您开始于:
df = pl.DataFrame(
{
"group": [1, 1, 1, 2, 2, 2, 2],
"mean": [25.5, 25.2, 24.9, 50.5, 55.1, 54.2, 60],
"std": [2.5, 3.5, 1.7, 10.2, 5.5, 7.8, 20.5],
}
)
我们可以这样做:
from scipy.special import ndtr
df=df.with_columns(j=pl.int_range(0,pl.count()).over('group')).lazy()
(df
.join(
df,
how='cross')
.filter((pl.col('group')==pl.col('group_right')) & (pl.col('j_right')!=pl.col('j')))
.with_columns(
left_smaller=ndtr(
(-(pl.col('mean')-pl.col('mean_right')))/
(pl.col('std').pow(2)+pl.col('std_right').pow(2)).pow(0.5))
)
.group_by(['group','j'])
.agg(probability_smallest_in_group=pl.col('left_smaller').product())
.join(df, on=['group','j'])
.select('group','mean','std','probability_smallest_in_group')
)
shape: (7, 4)
┌───────┬──────┬──────┬───────────────────────────────┐
│ group ┆ mean ┆ std ┆ probability_smallest_in_group │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ f64 ┆ f64 │
╞═══════╪══════╪══════╪═══════════════════════════════╡
│ 1 ┆ 25.5 ┆ 2.5 ┆ 0.198956 │
│ 1 ┆ 25.2 ┆ 3.5 ┆ 0.247683 │
│ 1 ┆ 24.9 ┆ 1.7 ┆ 0.30711 │
│ 2 ┆ 50.5 ┆ 10.2 ┆ 0.265239 │
│ 2 ┆ 55.1 ┆ 5.5 ┆ 0.094526 │
│ 2 ┆ 54.2 ┆ 7.8 ┆ 0.125587 │
│ 2 ┆ 60.0 ┆ 20.5 ┆ 0.054846 │
└───────┴──────┴──────┴───────────────────────────────┘
ndtr
函数来计算 0 点处的 cdf,这意味着左侧 rv 更小。它是一个 ufunc,因此可以直接与表达式一起使用(如上所示)。如果这太慢,您可以尝试通过将过滤器更改为仅采用 j_right>j
来利用对称性,获取这些行的 cdf,同时将其保存到中间变量。使用中间变量,您可以通过交换左右列并取 left_smaller
的补数来取回所有过滤的行。看起来像这样:
step1=(df
.join(
df,
how='cross')
.filter((pl.col('group')==pl.col('group_right')) & (pl.col('j_right')>pl.col('j')))
.with_columns(
left_smaller=ndtr(
(-(pl.col('mean')-pl.col('mean_right')))/
(pl.col('std').pow(2)+pl.col('std_right').pow(2)).pow(0.5))
))
(pl.concat([step1,
step1.select(
'group',
mean=pl.col('mean_right'),
std=pl.col('std_right'),
j=pl.col('j_right'),
group_right=pl.col('group'),
mean_right=pl.col('mean'),
std_right=pl.col('std'),
j_right=pl.col('j'),
left_smaller=1-pl.col('left_smaller')
)]).group_by(['group','j'])
.agg(probability_smallest_in_group=pl.col('left_smaller').product())
.join(df, on=['group','j'])
.select('group','mean','std','probability_smallest_in_group')
.collect()
)
shape: (7, 4)
┌───────┬──────┬──────┬───────────────────────────────┐
│ group ┆ mean ┆ std ┆ probability_smallest_in_group │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ f64 ┆ f64 │
╞═══════╪══════╪══════╪═══════════════════════════════╡
│ 1 ┆ 25.5 ┆ 2.5 ┆ 0.198956 │
│ 1 ┆ 25.2 ┆ 3.5 ┆ 0.247683 │
│ 1 ┆ 24.9 ┆ 1.7 ┆ 0.30711 │
│ 2 ┆ 50.5 ┆ 10.2 ┆ 0.265239 │
│ 2 ┆ 55.1 ┆ 5.5 ┆ 0.094526 │
│ 2 ┆ 54.2 ┆ 7.8 ┆ 0.125587 │
│ 2 ┆ 60.0 ┆ 20.5 ┆ 0.054846 │
└───────┴──────┴──────┴───────────────────────────────┘