我想生成要建模的数据。基于以下逻辑:
Input:2个名为z
和d
的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)
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个)
%%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个)
似乎您已经在这里完成了工作。现在,结果基于权衡取舍。以上所有解决方案均在不同程度上满足标准。
此代码是否可用于教别人?也许一天只执行一次或两次?它是大型项目的一部分,需要非常清楚地让其他人维护吗?如果是这种情况,请选择较慢但更易于理解和阅读的选项。
每天执行数千或数百万次吗?资源会花钱减少产品利润吗?如果是这样,请对其进行超级注释,并使用更快的选项。
basic mathematics
选项似乎是最好的折衷方案,因为它简单,易于理解,但执行速度很快。
我对每种方法的偏见:
dict case-when
:很慢,需要多次读取/测试才能完全了解实际发生的情况并弄清楚是否有未知陷阱。simple if-else
:很慢,需要多次读取/测试才能完全了解实际发生的情况并弄清楚是否有未知陷阱。basic mathematics
:即使您只有很小的数学背景(应该包括大多数程序员),也可以快速,容易理解。 np.where
:快速,完成工作,将需要多次读取/测试以完全了解实际发生的事情,但由于基于数组,因此不太容易发生问题。 供参考,这是编写代码的Python原理:
使用以上作为标准,可以更轻松地评估您的代码是否为pythonic。
我认为最快的选择是通过使用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
)映射为数组可以减少开销,并且仅生成一次正常数就可以减少运行时间。