繁体   English   中英

numpy - 使用numpy 1d数组的置换副本构建2d数组的最快方法

[英]numpy - fastest way to build 2d array with permuted copies of numpy 1d array

>>> import numpy as np
>>> a = np.arange(5)
>>> b = desired_function(a, 4)
array([[0, 3, 4, 1],
...    [1, 2, 1, 3],
...    [2, 4, 2, 4],
...    [3, 1, 3, 0],
...    [4, 0, 0, 2]])

到目前为止我尝试过的

def repeat_and_shuffle(a, ncols):
    nrows, = a.shape
    m = np.tile(a.reshape(nrows, 1), (1, ncols))
    return m

不知何故,我必须通过列有效地改变m[:,1:ncols]

这是创建这样一个数组的一种方法:

>>> a = np.arange(5)
>>> perms = np.argsort(np.random.rand(a.shape[0], 3), axis=0) # 3 columns
>>> np.hstack((a[:,np.newaxis], a[perms]))
array([[0, 3, 1, 4],
       [1, 2, 3, 0],
       [2, 1, 4, 1],
       [3, 4, 0, 3],
       [4, 0, 2, 2]])

这将创建所需形状的随机值数组,然后按其相应值对每列中的索引进行排序。 然后,使用该索引数组对a进行索引。

(使用的想法np.argsort创建置换索引列的一个阵列来自@ JME的答案在这里 。)

使用原始数组的随机排列构建新数组。

>>> a = np.arange(5)
>>> n = 4
>>> z = np.array([a]+[np.random.permutation(a) for _ in xrange(n-1)])
>>> z.T
array([[0, 0, 4, 3],
       [1, 1, 3, 0],
       [2, 3, 2, 4],
       [3, 2, 0, 2],
       [4, 4, 1, 1]])
>>> 

由于随机性,可能会出现重复的列。

这是Ashwini Chaudhary解决方案的一个版本:

>>> a = numpy.array(['a', 'b', 'c', 'd', 'e'])
>>> a = numpy.tile(a[:,None], 5)
>>> a[:,1:] = numpy.apply_along_axis(numpy.random.permutation, 0, a[:,1:])
>>> a
    array([['a', 'c', 'a', 'd', 'c'],
       ['b', 'd', 'b', 'e', 'a'],
       ['c', 'e', 'd', 'a', 'e'],
       ['d', 'a', 'e', 'b', 'd'],
       ['e', 'b', 'c', 'c', 'b']], 
      dtype='|S1')

我认为这是精心构思和教学上有用的(我希望他能够取消它)。 但是有些令人惊讶的是,它一直是我执行的测试中最慢的一个。 定义:

>>> def column_perms_along(a, cols):
...     a = numpy.tile(a[:,None], cols)
...     a[:,1:] = numpy.apply_along_axis(numpy.random.permutation, 0, a[:,1:])
...     return a
... 
>>> def column_perms_argsort(a, cols):
...     perms = np.argsort(np.random.rand(a.shape[0], cols - 1), axis=0)
...     return np.hstack((a[:,None], a[perms]))
... 
>>> def column_perms_lc(a, cols):
...     z = np.array([a] + [np.random.permutation(a) for _ in xrange(cols - 1)])
...     return z.T
... 

对于小数组和少数列:

>>> %timeit column_perms_along(a, 5)
1000 loops, best of 3: 272 µs per loop
>>> %timeit column_perms_argsort(a, 5)
10000 loops, best of 3: 23.7 µs per loop
>>> %timeit column_perms_lc(a, 5)
1000 loops, best of 3: 165 µs per loop

对于小型数组和许多列:

>>> %timeit column_perms_along(a, 500)
100 loops, best of 3: 29.8 ms per loop
>>> %timeit column_perms_argsort(a, 500)
10000 loops, best of 3: 185 µs per loop
>>> %timeit column_perms_lc(a, 500)
100 loops, best of 3: 11.7 ms per loop

对于大型数组和少数列:

>>> A = numpy.arange(1000)
>>> %timeit column_perms_along(A, 5)
1000 loops, best of 3: 2.97 ms per loop
>>> %timeit column_perms_argsort(A, 5)
1000 loops, best of 3: 447 µs per loop
>>> %timeit column_perms_lc(A, 5)
100 loops, best of 3: 2.27 ms per loop

对于大型数组和许多列:

>>> %timeit column_perms_along(A, 500)
1 loops, best of 3: 281 ms per loop
>>> %timeit column_perms_argsort(A, 500)
10 loops, best of 3: 71.5 ms per loop
>>> %timeit column_perms_lc(A, 500)
1 loops, best of 3: 269 ms per loop

故事的寓意:永远考验! 我想,对于非常大的数组,这样的n log n解决方案的缺点可能会变得很明显。 但根据我的经验, numpy的排序实现非常好。 我敢打赌,在注意到效果之前,您可能会上升几个数量级。

假设您最终打算循环遍历多个1D输入数组,您可能能够缓存排列索引,然后在使用时take而不是permute 即使1D阵列的长度不同,这也可以工作:您只需要丢弃过大的置换索引。

粗略(部分测试)的实现代码:

def permute_multi(X, k, _cache={}):
    """For 1D input `X` of len `n`, it generates an `(k,n)` array
    giving `k` permutations of `X`."""
    n = len(X)
    cached_inds = _cache.get('inds',np.array([[]]))

    # make sure that cached_inds has shape >= (k,n)
    if cached_inds.shape[1] < n:
        _cache['inds'] = cached_inds = np.empty(shape=(k,n),dtype=int)
        for i in xrange(k):
            cached_inds[i,:] = np.random.permutation(n)
    elif cached_inds.shape[0] < k:
        pass # TODO: need to generate more rows

    inds = cached_inds[:k,:] # dispose of excess rows

    if n < cached_inds.shape[1]:
        # dispose of high indices
        inds = inds.compress(inds.ravel()<n).reshape((k,n))

    return X[inds]

根据您的使用情况,您可能希望提供某种清除缓存的方法,或者至少提供一些可以在缓存的nk变得比大多数常用输入大得多时发现的启发式方法。 请注意,上面的函数给出(k,n)而不是(n,k) ,这是因为numpy默认行是连续的,并且我们希望n维是连续的-如果愿意,可以强制使用Fortran样式,或者转置输出(在数组中翻转一个标志而不是真正移动数据)。

关于此缓存概念在统计上是否有效,我相信在大多数情况下它可能很好,因为它大致等效于将函数开始时的种子重置为固定常量...但是如果您正在执行任何操作特别是对于返回的数组,在使用这种方法之前,您可能需要仔细考虑。

快速基准测试表明,(一旦预热) n=1000k=1000大约需要2.2 ms ,而np.random.permutation的完整k np.random.permutation需要150 ms 这大约快了70倍......但这是最简单的情况,我们不调用compress 对于n=999k=1000 ,具有升温用n=1000 ,它需要一个额外的数毫秒,给出8ms总时间,这仍然比快约19倍k -loop。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM