有没有一种方法可以将numpy随机值的一列添加到polars数组中,而其中一列是numpy.random的输入?

atmip9wb  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(167)

假设我有一个嵌套框架,其中有一个名为mean的列,我想将其用作随机数生成器的输入。来自R,这在管道中相对容易做到:

library(dplyr)

tibble(alpha = rnorm(1000),
       beta = rnorm(1000)) %>%
  mutate(mean = alpha + beta) %>%
  bind_cols(random_output = rnorm(n = nrow(.), mean = .$mean, sd = 1))
#> # A tibble: 1,000 × 4
#>     alpha   beta    mean random_output
#>     <dbl>  <dbl>   <dbl>         <dbl>
#>  1  0.231 -0.243 -0.0125         0.551
#>  2  0.213  0.647  0.861          0.668
#>  3  0.824 -0.353  0.471          0.852
#>  4  0.665 -0.916 -0.252         -1.81 
#>  5 -0.850  0.384 -0.465         -3.90 
#>  6  0.721  0.679  1.40           2.54 
#>  7  1.46   0.857  2.32           2.14 
#>  8 -0.242 -0.431 -0.673         -0.820
#>  9  0.234  0.188  0.422         -0.662
#> 10 -0.494 -2.15  -2.65          -3.01 
#> # ℹ 990 more rows

字符串
创建于2023-11-12使用reprex v2.0.2
在python中,我可以创建一个中间框架并将其用作np.random.normal()的输入,然后将其绑定到框架,但这感觉很笨拙。有没有一种方法可以将random_output col作为管道/链的一部分添加?

import polars as pl
import numpy as np

# create a df
df = (
    pl.DataFrame(
        {
            "alpha": np.random.standard_normal(1000),
            "beta": np.random.standard_normal(1000)
        }
    )
    .with_columns(
        (pl.col("alpha") + pl.col("beta")).alias("mean")
    )
    
)

# create an intermediate object
sim_vals = np.random.normal(df.get_column("mean"))

# bind the simulated values to the original df
(
    df.with_columns(random_output = pl.lit(sim_vals))
)
#> shape: (1_000, 4)
┌───────────┬───────────┬───────────┬───────────────┐
│ alpha     ┆ beta      ┆ mean      ┆ random_output │
│ ---       ┆ ---       ┆ ---       ┆ ---           │
│ f64       ┆ f64       ┆ f64       ┆ f64           │
╞═══════════╪═══════════╪═══════════╪═══════════════╡
│ -1.380249 ┆ 1.531959  ┆ 0.15171   ┆ 0.938207      │
│ -0.332023 ┆ -0.108255 ┆ -0.440277 ┆ 0.081628      │
│ -0.718319 ┆ -0.612187 ┆ -1.330506 ┆ -1.286229     │
│ 0.22067   ┆ -0.497258 ┆ -0.276588 ┆ 0.908147      │
│ …         ┆ …         ┆ …         ┆ …             │
│ 0.299117  ┆ -0.371846 ┆ -0.072729 ┆ 0.592632      │
│ 0.789633  ┆ 0.95712   ┆ 1.746753  ┆ 2.954801      │
│ -0.264415 ┆ -0.761634 ┆ -1.026049 ┆ -1.369753     │
│ 1.893911  ┆ 1.554736  ┆ 3.448647  ┆ 5.192537      │
└───────────┴───────────┴───────────┴───────────────┘

sh7euo9m

sh7euo9m1#

有四种方法(我能想到的),其中两种在评论中提到,一种我使用,最后一种我知道它存在,但没有亲自使用它。

第一个(get_column(col)或列'col'])引用

使用df.get_column作为np.random.normal的参数,如果使用pipe,则可以在链中执行此操作,例如

df.with_columns(
    mean=pl.col('alpha') + pl.col('beta')
).pipe(lambda df: (
    df.with_columns(
        rando=pl.lit(np.random.normal(df['mean']))
    )
))

字符串

秒(map_batches)

使用map_batches作为表达式

df.with_columns(
    mean=pl.col('alpha') + pl.col('beta')
).with_columns(
    rando=pl.col('mean').map_batches(lambda col: pl.Series(np.random.normal(col)))
)

第三次(numba)

这种方法比前两种方法更快,如果你要做很多随机化,但需要更多的设置(因此需要注意很多随机化)
numba允许您创建ufuncs,这些ufuncs是可以直接在表达式中使用的编译函数。
您可以创建此函数,它仅使用默认标准差

import numba as nb
@nb.guvectorize([(nb.float64[:], nb.float64[:])], '(n)->(n)', nopython=True)
def rando(means,  res):
    for i in range(len(means)):
        res[i]=np.random.normal(means[i])


那么你可以做

df.with_columns(
    mean=pl.col('alpha') + pl.col('beta')
).with_columns(rand_nb=rando(pl.col('mean')))


更多阅读:
使成向量
另一个Numba例子
limitation

第四个( rust 扩展)

不幸的是,对于这个答案(我想我自己一般),我没有涉足rust编程,但有一个扩展接口,你可以在rust中创建函数并将其部署为表达式。

性能

使用1 M行df我得到.
第一种方法:71.1 ms ± 8.06 ms/循环(7次运行的平均值±标准差,每次1个循环)
第二种方法:70.7 ms ± 7.88 ms/循环(7次运行的平均值±标准差,每次10个循环)
第三种方法:45.7 ms ± 2.86 ms/循环(7次运行的平均值±标准差,每次10个循环)
需要注意的一点是,它并不快,除非你想为每一行使用不同的平均值,例如...
df.with_columns(z=rando(pl.repeat(5,pl.count()))):43.8 ms ± 2.12 ms/循环(7次运行的平均值±标准差,每次10个循环)
df.with_columns(z=pl.Series(np.random.normal(5,1,df.shape[0]))):39.6 ms ± 3.64 ms/循环(7次运行的平均值±标准差,每次10个循环)

相关问题