如何将多个数组减少为一个?

How to reduce multiple arrays to one?

忽略我的数据结构,只给出一个包含多个 numpy 数组的列表(所有数组都完全相同 size/shape):

list[0] = [[0, 2, 0], 
           [1, 3, 0]]

list[1] = [[0, 0, 0], 
           [0, 0, 3]]

list[2] = [[5, 0, 0], 
           [0, 0, 0]]

我想将这个数组列表缩减为只有一个数组。零意味着没有价值。对于每个条目,应该采用唯一的给定值。没有重叠的值。对于给定位置,一个数组中始终只有一个分配值。

result: [[5, 2, 0]
         [1, 3, 3]]

在我的例子中,我有一个字典,其中元组作为键,数组作为值。数组是布尔数组。每个字典条目代表一个特殊的通道,并且在一个特定位置只有一个字典条目具有值True。 我现在想用字典键替换所有 True 值,并将该字典减少到只有一个数组。

例如(接近我的真实数据):

dict { (9, 2, 6): [[False, True],
                   [True, False]]
       (1, 5, 8): [[True, False],
                   [False, True]] }

result: [[(1, 5, 8),(9, 2, 6)]
         [(9, 2, 6),(1, 5, 8)]]

如何使用列表理解、numpy 函数 and/or map & reduce 来完成?


第一次尝试:

起初我以为我可以将我的 numpy 数组转换为 0 和 1 (.astype(np.float32)),然后将这些数组与我的密钥相乘:

values_filled = [key_tuple * value_array for key_tuple, value_array in dict]

然后对所有数组求和:

final = reduce(lambda right, left: right + left, values_filled)

但这显然行不通,因为我的键是值的元组而不仅仅是数字。


我尝试归档的是做以下操作的相反操作:

{color: np.all(mask, axis=-1) for (color, mask) in 
        ((color, segmentation == color) for color in colors) if mask.max()}

通过这个操作,我拍摄了一个分割图像并创建了一个具有预定义颜色的字典。 numpy 数组在颜色等于图像中该位置的颜色/等于字典的键的每个位置都有 True。

现在我想将这本字典缩小回图像(字典上有变化)。

这可以在没有任何花哨的情况下完成,只需使用旧的 for 循环和列表理解,然后枚举。我敢肯定那里有更好的衬垫或可以覆盖它的库,但这里有一个香草 Python 解决方案:

d = { (9, 2, 6): [[False, True],[True, False]],(1, 5, 8): [[True, False],[False, True]] }

new_list = []
for k,v in d.items():
    if new_list:
        for i, each in enumerate(v):
            x = [k if z else new_list[i][j] for j,z in enumerate(each)]
            new_list[i] = x
    else:
        for each in v:
            new_list.append([k if x else x for x in each])

print(new_list) # [[(1, 5, 8), (9, 2, 6)], [(9, 2, 6), (1, 5, 8)]]

P.S。也谢谢你的努力。

这是一个 Numpythonic 方法:

arr = np.dstack((lst1, lst2, lst3)) # Create columns along the first axis
mask = arr.astype(bool) # create a mask from places that are nonzero
mask2 = (~mask).all(axis=-1) # another mask that gives rows that are all zero
mask[mask2] = [True, False, False] # Truthify one optional item in all-zero rows 
result = arr[mask].reshape(lst1.shape) # get the desire items and reshape

演示:

In [135]: arr = np.dstack((lst1, lst2, lst3))

In [136]: arr
Out[136]: 
array([[[0, 0, 5],
        [2, 0, 0],
        [0, 0, 0]],

       [[1, 0, 0],
        [3, 0, 0],
        [0, 3, 0]]])

In [137]: mask = arr.astype(bool)

In [138]: mask2 = (~mask).all(axis=-1)

In [139]: mask[mask2] = [True, False, False]

In [140]: arr[mask].reshape(lst1.shape)
Out[140]: 
array([[5, 2, 0],
       [1, 3, 3]])

另一种 numpy 方法,如果你希望它成为一个普通的 numpy 数组:

import numpy as np

d = {(9, 2, 6): [[False, True],
                 [True, False]],
     (1, 5, 8): [[True, False],
                 [False, True]]}
x = np.sum(np.reshape(k, (1,1,-1)) * np.array(v)[..., None]  for k, v in d.items())
# x = np.sum(np.array(k)[None, None, :] * np.array(v)[..., None] for k, v in d.items())    # Alternative way
print(X)
# array([[[1, 5, 8],
#         [9, 2, 6]],
#        [[9, 2, 6],
#         [1, 5, 8]]])
np.all(x == np.array([[(1, 5, 8),(9, 2, 6)], [(9, 2, 6),(1, 5, 8)]]))
# True

这基本上使用了您在问题中概述的方法,即用值乘以真值掩码。我刚刚添加了一个事实,即该值的内容是另一个(第三)维度,并使用 numpys 广播功能来实现这一点。

你问题的第一部分只需要一个数组总和:

In [167]: alist = [[[0, 2, 0], 
     ...:            [1, 3, 0]],[[0, 0, 0], 
     ...:            [0, 0, 3]],[[5, 0, 0], 
     ...:            [0, 0, 0]]]
     ...:            
In [168]: alist
Out[168]: [[[0, 2, 0], [1, 3, 0]], [[0, 0, 0], [0, 0, 3]], [[5, 0, 0], [0, 0, 0]]]
In [169]: np.array(alist).shape
Out[169]: (3, 2, 3)
In [170]: np.array(alist).sum(axis=0)
Out[170]: 
array([[5, 2, 0],
       [1, 3, 3]])

这利用了 0 不影响总和并且没有任何重叠值这一事实。


您显然有第二个问题涉及布尔数组或掩码的字典。假设这与第一个问题有关,那么您只需要一种方法将这些掩码转换为第一个给出的数组(或列表)列表。


从字典开始,我们需要迭代键(或项)。我们可以使用相同的求和。经过一些实验后,我决定 I,J=np.where(v) 是将布尔掩码映射到目标数组的最简单方法:

In [200]: dd={ (9, 2, 6): [[False, True],
     ...:                    [True, False]],
     ...:        (1, 5, 8): [[True, False],
     ...:                    [False, True]] }
     ...:                    
In [201]: arr = np.zeros((2,2,3),int)
In [202]: for k,v in dd.items():
     ...:     I,J = np.where(v)
     ...:     arr[I,J,:] += k
     ...: 
In [203]: arr
Out[203]: 
array([[[1, 5, 8],
        [9, 2, 6]],

       [[9, 2, 6],
        [1, 5, 8]]])

最后一次迭代:

In [204]: k
Out[204]: (1, 5, 8)
In [205]: v
Out[205]: [[True, False], [False, True]]
In [206]: I,J=np.where(v)
In [207]: I,J
Out[207]: (array([0, 1]), array([0, 1]))
In [208]: arr[I,J,:]
Out[208]: 
array([[1, 5, 8],
       [1, 5, 8]])