我有一个功能
def getSamples():
p = lambda x : mlab.normpdf(x,3,2) + mlab.normpdf(x,-5,1)
q = lambda x : mlab.normpdf(x,5,14)
k=30
goodSamples = []
rightCount = 0
totalCount = 0
while(rightCount < 100000):
z0 = np.random.normal(5, 14)
u0 = np.random.uniform(0,k*q(z0))
if(p(z0) > u0):
goodSamples.append(z0)
rightCount += 1
totalCount += 1
return np.array(goodSamples)
我生成100000个样本的实现需要很长时间。如何使用itertools
或类似内容快速完成?
答案 0 :(得分:3)
我想说让代码更快的秘诀不在于改变循环语法。以下是几点:
np.random.normal
有一个额外的参数size
,可让您一次获得多个值。我建议使用一组说1E09
元素然后检查你的条件,看看有多少是好的。然后,您可以估计出这种可能性。sympy
对pdf进行符号评估? (我不知道这是否更快,但可能因为你已经知道了均值和方差。)p
可以使用符号函数吗?答案 1 :(得分:1)
一般来说,性能问题是由“错误的方式”做事造成的。 Numpy在使用时可以非常快,因为它被设计为使用,即通过利用其向量处理将这些向量化操作传递给编译代码。来自其他编程语言/方法的两个不良做法是
在这种情况下,很容易获得(大约)两个数量级的加速;权衡是更多的内存使用。
以下是一些代表性代码,不一定要盲目使用。我甚至没有验证它会产生正确的结果。它或多或少是您日常工作的直接翻译。您似乎使用拒绝方法从概率分布中绘制随机数。对于概率分布,可能有更有效的算法来执行此操作。
def getSamples2() :
p = lambda x : mlab.normpdf(x,3,2) + mlab.normpdf(x,-5,1)
q = lambda x : mlab.normpdf(x,5,14)
k=30
N = 100000 # Total number of samples we want
Ngood = 0 # Current number of good samples
goodSamples = np.zeros(N) # Storage for the good samples
while Ngood < N : # Unfortunately a loop, ....
z0 = np.random.normal(5, 14, size=N)
u0 = np.random.uniform(size=N)*k*q(z0)
ind, = np.where(p(z0) > u0)
n = min(len(ind), N-Ngood)
goodSamples[Ngood:Ngood+n] = z0[ind[:n]]
Ngood += n
return goodSamples
这会以块的形式生成随机数并保存好的数字。我没有尝试优化块大小(这里我只使用N
,我们想要的总数,原则上这可能/应该是不同的,甚至可以根据我们留下的数量进行调整)。不幸的是,这仍然使用循环,但现在这将运行“数十次”而不是100,000次。这也使用where
函数和数组切片;这些都是很好的通用工具。
在我的机器上使用%timeit
进行的一次测试中,我找到了
In [27]: %timeit getSamples() # Original routine
1 loops, best of 3: 49.3 s per loop
In [28]: %timeit getSamples2()
1 loops, best of 3: 505 ms per loop
答案 2 :(得分:0)
这里有点像“魔法”,但我不确定它可以提供帮助。对于性能来说,准备一个numpy数组(使用零)并填充它而不创建python自动增长列表可能要好得多。这是itertools和零准备。 (请原谅我未经测试的代码)
from itertools import count, ifilter, imap, takewhile
import operator
def getSamples():
p = lambda x : mlab.normpdf(x, 3, 2) + mlab.normpdf(x, -5, 1)
q = lambda x : mlab.normpdf(x, 5, 14)
k = 30
n = 100000
samples_iter = imap(
operator.itemgetter(1),
takewhile(
lambda i, s: i < n,
enumerate(
ifilter(lambda z: p(z) > np.random.uniform(0,k*q(z)),
(np.random.normal(5, 14) for _ in count()))
)))
goodSamples = numpy.zeros(n)
# set values from iterator, probably there is a better way for that
for i, sample in enumerate(samples_iter):
goodSamples[i] = sample
return goodSamples