Python 如何提高np.random.choice()循环效率

Python 如何提高np.random.choice()循环效率,python,python-3.x,numpy,vectorization,Python,Python 3.x,Numpy,Vectorization,我正在尝试将np.random.choice应用于具有不同权重的大数组,并想知道有什么方法可以避免循环并提高性能?在这里,体重可能有数百万 weights = [[0.1, 0.5, 0.4], [0.2, 0.4, 0.4], ... [0.3, 0.3, 0.4]] choice = [1, 2, 3] ret = np.zeros((len(weights), 20)) for i in range(len(weights

我正在尝试将np.random.choice应用于具有不同权重的大数组,并想知道有什么方法可以避免循环并提高性能?在这里,体重可能有数百万

weights = [[0.1, 0.5, 0.4],
           [0.2, 0.4, 0.4],
           ...
           [0.3, 0.3, 0.4]]

choice = [1, 2, 3]
ret = np.zeros((len(weights), 20))
for i in range(len(weights)):
    ret[i] = np.random.choice(choice, 20, p=weights[i])

看起来结果数组的每一行都独立于其他行。我不知道现在的表现有多糟糕。如果这真的是一个问题,我会尝试使用python的多处理模块来并行运行随机数生成。这应该会有所帮助。

看起来结果数组的每一行都独立于其他行。我不知道现在的表现有多糟糕。如果这真的是一个问题,我会尝试使用python的多处理模块来并行运行随机数生成。这应该会有帮助。

借鉴alongwith的思想,这里有一种矢量化的方法-

def random_choice_prob_vectorized(weights, num_items, choice=None):
    weights = np.asarray(weights)

    w = weights.cumsum(1)
    r = np.random.rand(len(weights),num_items)

    m,n = w.shape
    o = np.arange(m)[:,None]
    w_o = (w+o).ravel()
    r_o = (r+o).ravel()
    idx = np.searchsorted(w_o,r_o).reshape(m,-1)%n
    if choice is not None:
        return np.asarray(choice)[idx]
    else:
        return idx
要使用验证的示例运行-

借用alongwith的想法,这里有一种矢量化方法-

def random_choice_prob_vectorized(weights, num_items, choice=None):
    weights = np.asarray(weights)

    w = weights.cumsum(1)
    r = np.random.rand(len(weights),num_items)

    m,n = w.shape
    o = np.arange(m)[:,None]
    w_o = (w+o).ravel()
    r_o = (r+o).ravel()
    idx = np.searchsorted(w_o,r_o).reshape(m,-1)%n
    if choice is not None:
        return np.asarray(choice)[idx]
    else:
        return idx
要使用验证的示例运行-


以下是我的答案的概括:

p是一个二维数组,其行是概率向量。n是从每行定义的分布中提取的样本数。如果items为None,则样本是范围为0的整数,p.shape[1]。如果items不是None,则应为长度为p.shape[1]的序列

例如:

In [258]: p = np.array([[0.1, 0.5, 0.4], [0.75, 0, 0.25], [0, 0, 1], [1/3, 1/3, 1/3]])                                                   

In [259]: p                                                                                                                              
Out[259]: 
array([[0.1       , 0.5       , 0.4       ],
       [0.75      , 0.        , 0.25      ],
       [0.        , 0.        , 1.        ],
       [0.33333333, 0.33333333, 0.33333333]])

In [260]: vectorized_choice(p, 20)                                                                                                       
Out[260]: 
array([[1, 1, 2, 1, 1, 2, 2, 2, 1, 2, 1, 1, 1, 2, 2, 0, 1, 2, 2, 2],
       [0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
       [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
       [1, 0, 2, 2, 0, 1, 2, 1, 0, 0, 0, 0, 2, 2, 0, 0, 2, 1, 1, 2]])

In [261]: vectorized_choice(p, 20, items=[1, 2, 3])                                                                                      
Out[261]: 
array([[2, 1, 2, 2, 2, 3, 2, 2, 2, 2, 3, 3, 2, 2, 3, 3, 2, 3, 2, 2],
       [1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 3, 3, 1, 3, 1, 1, 1],
       [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
       [3, 3, 3, 1, 3, 2, 1, 2, 3, 1, 2, 2, 3, 2, 1, 2, 1, 2, 2, 2]])
形状为1000000的p的计时,3:

下面是Divakar函数的时间安排:

In [320]: %timeit random_choice_prob_vectorized(p, 20, choice=np.arange(1, p.shape[1]+1))
7.33 s ± 43.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
如果增加p中的列数,则差异将不那么明显,如果将列数设置得足够大,Divakar的函数将更快。例如

In [321]: p = np.random.rand(1000, 120)

In [322]: p /= p.sum(axis=1, keepdims=True)

In [323]: %timeit vectorized_choice(p, 20, items=np.arange(1, p.shape[1]+1))
6.41 ms ± 20.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [324]: %timeit random_choice_prob_vectorized(p, 20, choice=np.arange(1, p.shape[1]+1))
6.29 ms ± 342 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

以下是我的答案的概括:

p是一个二维数组,其行是概率向量。n是从每行定义的分布中提取的样本数。如果items为None,则样本是范围为0的整数,p.shape[1]。如果items不是None,则应为长度为p.shape[1]的序列

例如:

In [258]: p = np.array([[0.1, 0.5, 0.4], [0.75, 0, 0.25], [0, 0, 1], [1/3, 1/3, 1/3]])                                                   

In [259]: p                                                                                                                              
Out[259]: 
array([[0.1       , 0.5       , 0.4       ],
       [0.75      , 0.        , 0.25      ],
       [0.        , 0.        , 1.        ],
       [0.33333333, 0.33333333, 0.33333333]])

In [260]: vectorized_choice(p, 20)                                                                                                       
Out[260]: 
array([[1, 1, 2, 1, 1, 2, 2, 2, 1, 2, 1, 1, 1, 2, 2, 0, 1, 2, 2, 2],
       [0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
       [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
       [1, 0, 2, 2, 0, 1, 2, 1, 0, 0, 0, 0, 2, 2, 0, 0, 2, 1, 1, 2]])

In [261]: vectorized_choice(p, 20, items=[1, 2, 3])                                                                                      
Out[261]: 
array([[2, 1, 2, 2, 2, 3, 2, 2, 2, 2, 3, 3, 2, 2, 3, 3, 2, 3, 2, 2],
       [1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 3, 3, 1, 3, 1, 1, 1],
       [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
       [3, 3, 3, 1, 3, 2, 1, 2, 3, 1, 2, 2, 3, 2, 1, 2, 1, 2, 2, 2]])
形状为1000000的p的计时,3:

下面是Divakar函数的时间安排:

In [320]: %timeit random_choice_prob_vectorized(p, 20, choice=np.arange(1, p.shape[1]+1))
7.33 s ± 43.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
如果增加p中的列数,则差异将不那么明显,如果将列数设置得足够大,Divakar的函数将更快。例如

In [321]: p = np.random.rand(1000, 120)

In [322]: p /= p.sum(axis=1, keepdims=True)

In [323]: %timeit vectorized_choice(p, 20, items=np.arange(1, p.shape[1]+1))
6.41 ms ± 20.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [324]: %timeit random_choice_prob_vectorized(p, 20, choice=np.arange(1, p.shape[1]+1))
6.29 ms ± 342 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


恐怕你忍不住要绕圈。也许,你仍然可以通过使用numba来加快速度?有一件事可以帮助你,如果你有很多重复的权重,你可以用一个np.random.choice调用生成这些权重,然后把它放在正确的ret位置。另外,您可能需要更改:因为rangelenweights中的i:。。。对于i,权重在枚举权重中:。。。但这并不能给你带来速度,这只是一种良好的风格。@WarrenWeckesser他们会如何将项目数合并到链接的项目中?@Divakar,好吧,你知道,广播等等:好吧,它不是完全重复的。这是“重量”的形状?它可能对基准测试有用。恐怕你不得不循环。也许,你仍然可以通过使用numba来加快速度?有一件事可以帮助你,如果你有很多重复的权重,你可以用一个np.random.choice调用生成这些权重,然后把它放在正确的ret位置。另外,您可能需要更改:因为rangelenweights中的i:。。。对于i,权重在枚举权重中:。。。但这并不能给你带来速度,这只是一种良好的风格。@WarrenWeckesser他们会如何将项目数合并到链接的项目中?@Divakar,好吧,你知道,广播等等:好吧,它不是完全重复的。这是“重量”的形状?这可能对基准测试很有用,很好地了解时间安排!有道理。更新了我的解决方案。没有你的好,但比我以前的好得多。你能麻烦更新一下时间吗?谢谢@Divakar,好的,但在我的测试中,新版本实际上比旧版本慢一点谢谢,但在矢量化选择中,概率矩阵应该是p,对吗?@Nick,是的,修正了。很高兴看到计时!有道理。更新了我的解决方案。没有你的好,但比我以前的好得多。你能麻烦更新一下时间吗?谢谢@Divakar,好的,但在我的测试中,新版本实际上比旧版本慢一点谢谢,但在矢量化选择中,概率矩阵应该是p,对吗?@Nick,是的,修正了。