Python 为什么numpy.random.choice这么慢?

Python 为什么numpy.random.choice这么慢?,python,random,numpy,Python,Random,Numpy,在编写脚本时,我发现了numpy.random.choice函数。我实现它是因为它比等价的if语句干净得多。然而,在运行脚本之后,我意识到它比if语句要慢得多 以下是一个MWE。第一种方法需要0.0秒,而第二种方法需要7.2秒。如果你放大i循环,你会看到random.choice的速度减慢了多少 有人能评论一下为什么random.choice会慢得多吗 import numpy as np import numpy.random as rand import time as tm #-----

在编写脚本时,我发现了numpy.random.choice函数。我实现它是因为它比等价的if语句干净得多。然而,在运行脚本之后,我意识到它比if语句要慢得多

以下是一个MWE。第一种方法需要0.0秒,而第二种方法需要7.2秒。如果你放大i循环,你会看到random.choice的速度减慢了多少

有人能评论一下为什么random.choice会慢得多吗

import numpy as np
import numpy.random as rand
import time as tm

#-------------------------------------------------------------------------------

tStart = tm.time()
for i in xrange(100):
    for j in xrange(1000):
        tmp = rand.rand()
        if tmp < 0.25:
            var = 1
        elif tmp < 0.5:
            var = -1
print('Time: %.1f s' %(tm.time() - tStart))

#-------------------------------------------------------------------------------

tStart = tm.time()
for i in xrange(100):
    for j in xrange(1000):
        var = rand.choice([-1, 0, 1], p = [0.25, 0.5, 0.25])
print('Time: %.1f s' %(tm.time() - tStart))
将numpy导入为np
将numpy.random导入为rand
将时间导入为tm
#-------------------------------------------------------------------------------
tStart=tm.time()
对于X范围内的i(100):
对于X范围内的j(1000):
tmp=rand.rand()
如果tmp<0.25:
var=1
elif tmp<0.5:
var=-1
打印('Time:%.1f s%%(tm.Time()-tStart))
#-------------------------------------------------------------------------------
tStart=tm.time()
对于X范围内的i(100):
对于X范围内的j(1000):
var=rand.choice([-1,0,1],p=[0.25,0.5,0.25])
打印('Time:%.1f s%%(tm.Time()-tStart))

你用错了。将操作矢量化,否则numpy将不会提供任何好处:

var = numpy.random.choice([-1, 0, 1], size=1000, p=[0.25, 0.5, 0.25])
定时数据:

>>> timeit.timeit('''numpy.random.choice([-1, 0, 1],
...                                      size=1000,
...                                      p=[0.25, 0.5, 0.25])''',
...               'import numpy', number=10000)
2.380380242513752

>>> timeit.timeit('''
... var = []
... for i in xrange(1000):
...     tmp = rand.rand()
...     if tmp < 0.25:
...         var.append(1)
...     elif tmp < 0.5:
...         var.append(-1)
...     else:
...         var.append(0)''',
... setup='import numpy.random as rand', number=10000)
5.673041396894519
>>timeit.timeit('''numpy.random.choice([-1,0,1],
…尺寸=1000,
…p=[0.25,0.5,0.25])”,
…“导入numpy”,编号=10000)
2.380380242513752
>>>timeit,timeit(“”)
…var=[]
…对于X范围内的i(1000):
…tmp=rand.rand()
…如果tmp<0.25:
…变量附加(1)
…如果tmp<0.5:
…变量附加(-1)
……其他:
…变量追加(0)“”,
…setup='import numpy.random as rand',number=10000)
5.673041396894519

我怀疑
np.random.choice的普遍性正在减慢它的速度,小样本的速度要比大样本慢得多

如果
版本为:

def foo(n):
    x = np.random.rand(n)
    var = np.zeros(n)
    var[x<.25] = -1
    var[x>.75] = 1
    return var

因此,对于
1000
大小,
choice
慢3-4倍,但向量越大,差异就越小。

我花了很长时间才发现,由于通过
np.random.choice
进行随机键采样,我的数据生成器速度非常慢

如果不需要非均匀分布,那么这里是我找到的可行解决方案

替换

def get_random_key(a_huge_key_list):
    return np.random.choice(a_huge_key_list)


它的加速比为x60

此解决方案的累积分数大约快25倍:

def choice(options,probs):
    x = np.random.rand()
    cum = 0
    for i,p in enumerate(probs):
        cum += p
        if x < cum:
            break
    return options[i]


options = ['a','b','c','d']
probs = [0.2,0.6,0.15,0.05]
runs = 100000


now = time.time()
temp = []
for i in range(runs):
    op = choice(options,probs)
    temp.append(op)
temp = Counter(temp)
for op,x in temp.items():
    print(op,x/runs)
print(time.time()-now)

print("")
now = time.time()
temp = []
for i in range(runs):
    op = np.random.choice(options,p = probs)
    temp.append(op)
temp = Counter(temp)
for op,x in temp.items():
    print(op,x/runs)
print(time.time()-now)

这真的不是一个公平的比较。每一次,numpy都必须获取p列表的累积和,将其放入一个新的向量中,然后对其进行迭代。通过知道只有三个变量,第一个变量和第三个变量之和为0.5,可以有效地进行预处理。除此之外,如下文所述,numpy针对矢量化操作进行了优化,而不是对单个简单操作进行数千次的优化。此外,请单独使用
timeit
,而不是
time
。有关在numpy 1.8.1中从10000^2中选择40000的信息,请参阅一段时间。如前所述,您是在对苹果进行比较吗?第一个计算10^3*10^4=10^7个随机数,而第二个计算10^2*10^3*10^4=10^9个随机数,不是吗;有趣。你知道为什么这会对速度产生如此大的影响吗?从外观上看,您的扩展版本与NumPy已经在做的事情没有太大区别。啊,经过一些测试,我怀疑您看到的时间差异的主要原因是您使用了一个实际的Python
list
作为输入,
np.random.choice
首先隐式地将其转换为NumPy数组,而第二个版本的
get_random_key
避免了这种转换。列表到数组的转换成为大型列表的瓶颈。当我在一个包含10**6个元素(而不是一个列表)的1d NumPy数组上测试这两个变量时,计时对我来说更为接近:第二个版本大约快了50%。小重构:
python def fast_choice(options,probs):x=random.random()#np.random.rand()cum=0表示枚举中的I,p(probs):cum+=p如果x
def get_random_key(a_huge_key_list):
    L = len(a_huge_key_list)
    i = np.random.randint(0, L)
    return a_huge_key_list[i]
def choice(options,probs):
    x = np.random.rand()
    cum = 0
    for i,p in enumerate(probs):
        cum += p
        if x < cum:
            break
    return options[i]


options = ['a','b','c','d']
probs = [0.2,0.6,0.15,0.05]
runs = 100000


now = time.time()
temp = []
for i in range(runs):
    op = choice(options,probs)
    temp.append(op)
temp = Counter(temp)
for op,x in temp.items():
    print(op,x/runs)
print(time.time()-now)

print("")
now = time.time()
temp = []
for i in range(runs):
    op = np.random.choice(options,p = probs)
    temp.append(op)
temp = Counter(temp)
for op,x in temp.items():
    print(op,x/runs)
print(time.time()-now)
b 0.59891
a 0.20121
c 0.15007
d 0.04981
0.16232800483703613

b 0.5996
a 0.20138
c 0.14856
d 0.05046
3.8451428413391113