【问题标题】:Python shuffle array that has very few non zeros (very sparsey)Python shuffle 数组,非零很少(非常稀疏)
【发布时间】:2017-09-06 12:21:08
【问题描述】:

我有一个非常大(长度约为 1.5 亿)的 numpy 数组,它的非零值非常少(大约 99.9% 的数组是 0)。我想洗牌,但洗牌很慢(大约需要 10 秒,这是不可接受的,因为我正在做蒙特卡洛模拟)。有没有办法以一种考虑到我的数组主要由 0 组成的事实的方式对其进行洗牌?

我正在考虑只对我的正值进行洗牌,然后将其随机插入一个充满 0 的数组中,但我找不到一个 numpy 函数。

【问题讨论】:

  • 您只需要numpy 方法吗?
  • 您是否将数组存储为稀疏矩阵?这样可以防止每次洗牌时写入一个大部分为零的巨大矩阵。
  • @Chris_Rands 不一定,但我希望用 C 语言编写一些函数以提高速度

标签: python arrays numpy


【解决方案1】:

类似于@Divakar 的方法,但使用scipy.sparse

a = scipy.sparse.coo_matrix(a)

def shuffle_sparse_coo(a):
    a.col = np.random.choice(a.shape[1], a.nnz, replace=0)
    return a

shuffle_sparse_coo(a).todense() # Using Divakar's 'a' array
Out[408]: matrix([[0, 8, 0, 0, 7, 0, 0, 0, 0, 4, 0, 0, 5, 0, 3, 0, 1, 0, 0, 0]])

编辑:

如果你想保持密集,我敢肯定这甚至比 @Divakar 的 hackish 方法差不了多少:

%timeit shuffle_sparse_arr_hackish(a)
The slowest run took 4.32 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 44.7 µs per loop

def shuffle_sparse_arr_nz(a):
    out = np.zeros_like(a)
    mask = np.nonzero(a)
    idx = np.random.choice(a.size, mask[0].size, replace=0)
    out[idx] = a[mask]
    return out

%timeit shuffle_sparse_arr_nz(a)
The slowest run took 4.68 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 41 µs per loop

EDIT2:

在稀疏方法中实现@Divakar 的破解:

def shuffle_sparse_coo_h(a):
    idx = np.unique((a.shape[1]*np.random.rand(2*a.nnz)).astype(int))[:a.nnz]
    while idx.size<n:
        idx = np.unique((a.shape[1]*np.random.rand(2*a.nnz)).astype(int))[:a.nnz]
    a.col = idx
    return a

#using a 15m element a:

%timeit shuffle_sparse_arr_hackish(a)
10 loops, best of 3: 52.8 ms per loop

a1 = sparse.coo_matrix(a)
%timeit shuffle_sparse_coo(a1)
1 loop, best of 3: 1.01 s per loop

%timeit shuffle_sparse_coo_h(a1)
The slowest run took 4.58 times longer than the fastest. This could mean that an intermediate result is being cached.
100 loops, best of 3: 2.02 ms per loop

EDIT2:

使用np.random.randint进一步改进

def shuffle_sparse_coo_h2(a):
    idx = np.unique(np.random.randint(0,a.shape[1],(2*a.nnz,)))[:a.nnz]
    while idx.size < n:
        idx = np.unique(np.random.randint(0,a.shape[1],(2*a.nnz,)))[:a.nnz]
    a.col = idx
    return a  

%timeit shuffle_sparse_coo_h2(a1)
1000 loops, best of 3: 1.86 ms per loop

【讨论】:

  • 不错!如果他们需要一个数组:shuffle_sparse_coo(a).todense().A1.
  • 我相信在大型阵列上,比如在我的计时测试中测试的 1500 万个,shuffle_sparse_arr_nz 将与 shuffle_sparse_arr 相媲美,并且这种骇人听闻的方式会大放异彩。
  • 根据我的进一步测试,你是对的。
  • 奇怪,它甚至比用np.nonzeron=mask[0].size hack 还要快
【解决方案2】:

方法#1:这是一种方法 -

def shuffle_sparse_arr(a):
    out = np.zeros_like(a)
    mask = a!=0
    n = np.count_nonzero(mask)
    idx = np.random.choice(a.size, n, replace=0)
    out[idx] = a[mask]
    return out

方法 #2: 黑客方式 -

def shuffle_sparse_arr_hackish(a):
    out = np.zeros_like(a)
    mask = a!=0
    n = np.count_nonzero(mask)
    idx = np.unique((a.size*np.random.rand(2*n)).astype(int))[:n]
    while idx.size<n:
        idx = np.unique((a.size*np.random.rand(2*n)).astype(int))[:n]
    np.random.shuffle(idx)
    out[idx] = a[mask]
    return out

样本运行 -

In [269]: # Setup input array
     ...: a = np.zeros((20),dtype=int)
     ...: sidx = np.random.choice(a.size, 6, replace=0)
     ...: a[sidx] = [5,8,4,1,7,3]
     ...: 

In [270]: a
Out[270]: array([4, 0, 0, 8, 0, 0, 5, 0, 0, 0, 0, 7, 0, 0, 1, 0, 0, 0, 0, 3])

In [271]: shuffle_sparse_arr(a)
Out[271]: array([0, 5, 0, 0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 7, 3, 8, 0, 0])

In [272]: shuffle_sparse_arr_hackish(a)
Out[272]: array([3, 1, 5, 0, 4, 0, 7, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0])

运行时测试 -

In [288]: # Setup input array with 15 million and 99.9% zeros
     ...: a = np.zeros((15000000),dtype=int)
     ...: 
     ...: # Set 100-99.9% as random non-zeros
     ...: n = int(a.size*((100-99.9)/100)) 
     ...: 
     ...: set_idx = np.random.choice(a.size, n , replace=0)
     ...: nums = np.random.choice(a.size, n , replace=0)
     ...: a[set_idx] = nums
     ...: 

In [289]: %timeit shuffle_sparse_arr(a)
1 loops, best of 3: 647 ms per loop

In [290]: %timeit shuffle_sparse_arr_hackish(a)
10 loops, best of 3: 29.1 ms per loop

In [291]: %timeit np.random.shuffle(a)
1 loops, best of 3: 606 ms per loop

【讨论】:

  • 抱歉,我的问题不清楚:非零值并不完全相同。
  • 你可能想看看我的 scipy.sparse 方法,灵感来自 @Divakar 的下面
  • @BillyBoy 瓶颈似乎是生成那些独特的索引。因此,可以考虑使用一些低级的东西,例如numba 甚至cython
  • mask=np.nonzero(a) 会不会更快?
  • 在你这边测试一下,在我的机器上似乎快了很多。貌似长步是mask.sum(),如果你做np.nonzero可以换成len(mask)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-30
相关资源
最近更新 更多