繁体   English   中英

numpy将3维数组索引为2维数组

[英]Numpy indexing 3-dimensional array into 2-dimensional array

我有以下结构的三维数组:

x = np.array([[[1,2],
               [3,4]],
              [[5,6],
               [7,8]]], dtype=np.double)

另外,我有一个索引数组

idx = np.array([[0,1],[1,3]], dtype=np.int)

idx每一行定义行/列索引,用于将每个子数组沿x0轴放置到二维数组K ,该数组初始化为

K = np.zeros((4,4), dtype=np.double)

我想使用花式索引/广播来执行索引而无需for循环。 我目前以这种方式:

for i, id in enumerate(idx):

    idx_grid = np.ix_(id,id)

    K[idx_grid] += x[i]

这样的结果是:

>>> K = array([[ 1.,  2.,  0.,  0.],
               [ 3.,  9.,  0.,  6.],
               [ 0.,  0.,  0.,  0.],
               [ 0.,  7.,  0.,  8.]])

这可能与花式索引有关吗?

这是另一种方法。 使用xidxK定义为您的问题:

indices = (idx[:,None] + K.shape[1]*idx).ravel('f')
np.add.at(K.ravel(), indices, x.ravel())

然后我们有:

>>> K
array([[ 1.,  2.,  0.,  0.],
       [ 3.,  9.,  0.,  6.],
       [ 0.,  0.,  0.,  0.],
       [ 0.,  7.,  0.,  8.]])

要在NumPy数组上执行无缓冲的就地加法,您需要使用np.add.at (以避免在for循环中使用+= )。

但是,将2D索引数组的列表以及要在这些索引处添加的对应数组传递给np.add.at 这是因为该函数将这些数组列表解释为高维数组,并且会引发IndexErrors。

传递一维数组要简单得多。 您可以暂时拉开Kx以得到一维零的数组和一维值数组以添加到这些零。 唯一的麻烦是从idx构造一个相应的索引一维数组,在该数组中添加值。 如上所示,这可以通过与算术运算符进行广播然后进行破坏来完成。

预期的操作是x到由idx索引的位置中的值的accumulation之一。 您可以将这些idx位置视为直方图数据的bins ,将x值视为需要为这些bin累加的权重。 现在,要执行这种装箱操作,可以使用np.bincount 这是一个这样的实现-

# Get size info of expected output
N = idx.max()+1

# Extend idx to cover two axes, equivalent to `np.ix_`
idx1 = idx[:,None,:] + N*idx[:,:,None]

# "Accumulate" values from x into places indexed by idx1
K = np.bincount(idx1.ravel(),x.ravel()).reshape(N,N)

运行时测试-

1)创建输入:

In [361]: # Create x and idx, with idx having unique elements in each row of idx, 
     ...: # as otherwise the intended operation is not clear
     ...: 
     ...: nrows = 100
     ...: max_idx = 100
     ...: ncols_idx = 2
     ...: 
     ...: x = np.random.rand(nrows,ncols_idx,ncols_idx)
     ...: idx = np.random.randint(0,max_idx,(nrows,ncols_idx))
     ...: 
     ...: valid_mask = ~np.any(np.diff(np.sort(idx,axis=1),axis=1)==0,axis=1)
     ...: 
     ...: x = x[valid_mask]
     ...: idx = idx[valid_mask]
     ...: 

2)定义功能:

In [362]: # Define the original and proposed (bincount based) approaches
     ...: 
     ...: def org_approach(x,idx):
     ...:   N = idx.max()+1
     ...:   K = np.zeros((N,N), dtype=np.double)
     ...:   for i, id in enumerate(idx):    
     ...:       idx_grid = np.ix_(id,id)    
     ...:       K[idx_grid] += x[i]         
     ...:   return K
     ...: 
     ...: 
     ...: def bincount_approach(x,idx):
     ...:   N = idx.max()+1
     ...:   idx1 = idx[:,None,:] + N*idx[:,:,None]
     ...:   return np.bincount(idx1.ravel(),x.ravel()).reshape(N,N)
     ...: 

3)最后对它们进行计时:

In [363]: %timeit org_approach(x,idx)
100 loops, best of 3: 2.13 ms per loop

In [364]: %timeit bincount_approach(x,idx)
10000 loops, best of 3: 32 µs per loop

我认为这不可能有效,因为循环中有+= 这意味着,您将不得不将数组idx “放大”一维,然后利用np.sum(x[...], axis=...)再次减小它。 较小的优化将是:

import numpy as np

xx = np.array([[[1, 2],
               [3, 4]],
              [[5, 6],
               [7, 8]]], dtype=np.double)

idx = np.array([[0, 1], [1, 3]], dtype=np.int)

K0, K1 = np.zeros((4, 4), dtype=np.double), np.zeros((4, 4), dtype=np.double)

for k, i in enumerate(idx):
    idx_grid = np.ix_(i, i)
    K0[idx_grid] += xx[k]

for x, i in zip(xx, idx):
    K1[np.ix_(i, i)] += x

print("K1 == K0:", np.allclose(K1, K0))  # prints: K1 == K0: True

PS:请勿将id用作变量名,因为它是Python关键字。

暂无
暂无

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

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