使用numpy在有条件的情况下生成随机数据

问题描述 投票:2回答:2

问题

我想生成要建模的数据。基于以下逻辑:

Input:2个名为zd的numpy数组。

z:1d数组,值0/1

d:1d数组,值0/1

返回:y:一维数组。值:范数随机数。

如果z == 0且d == 0,则y〜norm(1,1),

如果z == 0且d == 1,则y〜norm(0,1),

如果z == 1且d == 0,则y〜norm(1,1),

如果z == 1且d == 1,则y〜norm(2,1)。

我想以一种超快速,清晰和Python的方式做到这一点。

[似乎是基础数学,np.where更快。在这种情况下,我只有3个条件(您可以从基本数学部分清楚地看到)。如果我有10个或更多条件,则在if-else语句中键入它们有时会造成混淆。我想执行数据模拟,这意味着我将以不同的n生成数百万次的数据。那么,什么是最好的方法呢?

我尝试过的:

# generate data
n = 2000
z = np.random.binomial(1,0.5,n)
d = np.random.binomial(1,0.5,n)

dict case-when

def myfun(x):
    return {(0,1):np.random.normal(0,1),\
            (0,0):np.random.normal(1,1),\
            (1,0):np.random.normal(1,1),\
            (1,1):np.random.normal(2,1)}[x]
%%timeit
y = [myfun(i) for i in zip(z,d)]

输出:

每个循环16.2 ms±139 µs(平均±标准偏差,共运行7次,每个循环100个循环)

简单if-else

%%timeit
y = np.random.normal([0 if (i == 0) & (j ==1) else 2 if (i == 1) & (j == 1) else 1 for i,j in zip(z,d)],1)

输出:

每个循环1.38 ms±22.1 µs(平均±标准偏差,共运行7次,每个循环1000个)

基础数学

%%timeit
h0 = np.random.normal(0,1,n)
h1 = np.random.normal(1,1,n)
h2 = np.random.normal(2,1,n)
y = (1-z)*d*h0 + (1-d)*h1 + z*d*h2

输出:

每个循环140 µs±135 ns(平均±标准偏差,共运行7次,每个循环10000个)

np.where

%%timeit
h0 = np.random.normal(0,1,n)
h1 = np.random.normal(1,1,n)
h2 = np.random.normal(2,1,n)
y = np.where((d== 0),h1,0) + np.where((z ==1) & (d== 1),h2,0) + np.where((z ==0) & (d== 1),h0,0)

输出:

每个循环156 µs±598 ns(平均±标准偏差,共运行7次,每个循环10000个)

python numpy if-statement
2个回答
1
投票

似乎您已经在这里完成了工作。现在,结果基于权衡取舍。以上所有解决方案均在不同程度上满足标准。

此代码是否可用于教别人?也许一天只执行一次或两次?它是大型项目的一部分,需要非常清楚地让其他人维护吗?如果是这种情况,请选择较慢但更易于理解和阅读的选项。

每天执行数千或数百万次吗?资源会花钱减少产品利润吗?如果是这样,请对其进行超级注释,并使用更快的选项。

basic mathematics选项似乎是最好的折衷方案,因为它简单,易于理解,但执行速度很快。

我对每种方法的偏见:

  • dict case-when:很慢,需要多次读取/测试才能完全了解实际发生的情况并弄清楚是否有未知陷阱。
  • simple if-else:很慢,需要多次读取/测试才能完全了解实际发生的情况并弄清楚是否有未知陷阱。
  • basic mathematics:即使您只有很小的数学背景(应该包括大多数程序员),也可以快速,容易理解。
  • np.where:快速,完成工作,将需要多次读取/测试以完全了解实际发生的事情,但由于基于数组,因此不太容易发生问题。

供参考,这是编写代码的Python原理:

  • 美丽胜于丑陋。
  • 显式优于隐式。
  • 简单胜于复杂。
  • 复杂胜于复杂。
  • 平面比嵌套更好。
  • 稀疏胜于稠密。
  • 可读性计数。

使用以上作为标准,可以更轻松地评估您的代码是否为pythonic。


0
投票

我认为最快的选择是通过使用normal的数组值参数仅生成一次随机数。使用新的随机API:

import numpy as np

rng = np.random.default_rng()

# generate data
n = 2000
z = rng.binomial(1, 0.5, n)
d = rng.binomial(1, 0.5, n)

def generate_once(z, d):
    """Generate randoms for https://stackoverflow.com/questions/59676147"""

    # encode mean; scale is always 1 anyway in the example
    means = np.zeros_like(z, dtype=float)
    z_inds = z == 0
    d_inds = d == 0
    means[d_inds] = 1
    means[z_inds & ~d_inds] = 2

    # generate the data
    y = rng.normal(means)
    return y

y = generate_once(z, d)

我并没有尝试将其与其他所有应用都进行计时,但是我希望这具有竞争力。考虑一下它是if-else的一个更快的变体。使用快捷方式将means(通常也包括scales)映射为数组可以减少开销,并且仅生成一次正常数就可以减少运行时间。

© www.soinside.com 2019 - 2024. All rights reserved.