python - Numpy 根据索引求和多维数组中的元素

标签 python arrays numpy pytorch memory-efficient

我正在处理非常大的多维数据,但让我以二维数组为例。给定一个每次迭代都会改变的值数组,

arr = np.array([[ 1, 2, 3, 4, 5], [5, 6, 7, 8, 9]]) # a*b

和一个始终固定的索引数组。

idx = np.array([[[0, 1, 1], [-1, -1, -1]],
                [[5, 1, 3], [1, -1, -1]]]) # n*h*w, where n = a*b,

这里-1表示不应用索引。我希望得到一个结果

res = np.array([[1+2+2, 0],
                [5+2+4, 2]]) # h*w

在实际实践中,我正在使用非常大的 3D 张量(n ~ 万亿),并且 idx 非常稀疏(即大量 -1)。由于 idx 是固定的,我目前的解决方案是通过填充 0 和 1 来预先计算一个 n*(h*w) 数组 index_tensor,然后执行

tmp = arr.reshape(1, n)
res = (tmp @ index_tensor).reshape([h,w])

它工作正常,但需要大量内存来存储index_tensor。有没有什么方法可以利用 idx 的稀疏性和不可更改性来降低内存成本并在 python 中保持公平的运行速度(使用 numpy 或 pytorch 是最好的)?提前致谢!

最佳答案

忽略-1目前比较复杂,直接的索引和求和是:

In [58]: arr = np.array([[ 1, 2, 3, 4, 5], [5, 6, 7, 8, 9]])
In [59]: idx = np.array([[[0, 1, 1], [2, 4, 6]],
    ...:                 [[5, 1, 3], [1, -1, -1]]])
In [60]: arr.flat[idx]
Out[60]: 
array([[[1, 2, 2],
        [3, 5, 6]],

       [[5, 2, 4],
        [2, 9, 9]]])
In [61]: _.sum(axis=-1)
Out[61]: 
array([[ 5, 14],
       [11, 20]])

处理 -1 的一种方法(不一定快速或内存高效)是使用掩码数组:

In [62]: mask = idx<0
In [63]: mask
Out[63]: 
array([[[False, False, False],
        [False, False, False]],

       [[False, False, False],
        [False,  True,  True]]])

In [65]: ma = np.ma.masked_array(Out[60],mask)
In [67]: ma
Out[67]: 
masked_array(
  data=[[[1, 2, 2],
         [3, 5, 6]],

        [[5, 2, 4],
         [2, --, --]]],
  mask=[[[False, False, False],
         [False, False, False]],

        [[False, False, False],
         [False,  True,  True]]],
  fill_value=999999)
In [68]: ma.sum(axis=-1)
Out[68]: 
masked_array(
  data=[[5, 14],
        [11, 2]],
  mask=[[False, False],
        [False, False]],
  fill_value=999999)

掩码数组通过用中性值替换掩码值来处理求和之类的操作,例如求和时用 0 代替。

(我可能会在早上再看一遍)。

与矩阵乘积相加

In [72]: np.einsum('ijk,ijk->ij',Out[60],~mask)
Out[72]: 
array([[ 5, 14],
       [11,  2]])

这比掩码数组方法更直接、更快。

您还没有详细说明如何构建 index_tensor所以我不会尝试比较它。

另一种可能性是用 0 填充数组,并调整索引:

In [83]: arr1 = np.hstack((0,arr.ravel()))
In [84]: arr1
Out[84]: array([0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9])
In [85]: arr1[idx+1]
Out[85]: 
array([[[1, 2, 2],
        [3, 5, 6]],

       [[5, 2, 4],
        [2, 0, 0]]])
In [86]: arr1[idx+1].sum(axis=-1)
Out[86]: 
array([[ 5, 14],
       [11,  2]])

稀疏

首次尝试使用稀疏矩阵:

reshape idx到二维:

In [141]: idx1 = np.reshape(idx,(4,3))

从中创建一个稀疏张量。首先,我将迭代 lil方法,尽管通常构建 coo (甚至 csr )直接输入更快:

In [142]: M = sparse.lil_matrix((4,10),dtype=int)
     ...: for i in range(4):
     ...:     for j in range(3):
     ...:         v = idx1[i,j]
     ...:         if v>=0:
     ...:            M[i,v] = 1
     ...: 
In [143]: M
Out[143]: 
<4x10 sparse matrix of type '<class 'numpy.int64'>'
    with 9 stored elements in List of Lists format>
In [144]: M.A
Out[144]: 
array([[1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 0, 1, 0, 1, 0, 0, 0],
       [0, 1, 0, 1, 0, 1, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]])

然后可以将其用于求乘积之和:

In [145]: <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e1aca1809393cf938097848d" rel="noreferrer noopener nofollow">[email protected]</a>()
Out[145]: array([ 3, 14, 11,  2])

使用<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="cf82e18e8faebdbde1bdaeb9aaa3" rel="noreferrer noopener nofollow">[email protected]</a>()本质上就是你所做的。而M稀疏,arr不是。对于本例M.A@M@ 快.

关于python - Numpy 根据索引求和多维数组中的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65420433/

相关文章:

PHP:检查数组键是否有值

php - 从php中的多级数组中获取数组名称

python - boost.python 参数类型不匹配(numpy.int64 -> int)

python - 如何显示 pandas 值计数的千位分隔符?

python - 如何使用 GDB 在 C 级别调试 python 脚本。给我一个简单的例子

python - PyQt5 使用 keyPressEvent() 触发 paintEvent()

python - Gnuplot 中的连续、大小固定、透明的图形

python - 使用未定义的键将列表添加到 for 循环中的字典键

c# - 过滤列表条目

python - 如何索引列表/numpy 数组以便使用 matplotlib 绘制数据