【问题标题】:How to reduce multiple arrays to one?如何将多个数组减少为一个?
【发布时间】:2018-08-03 18:44:27
【问题描述】:

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

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]]

在我的例子中,我有一个字典,其中元组作为键,数组作为值。数组是布尔数组。每个 dict 条目代表一个特殊通道,并且在一个特定位置,只有一个 dict 条目具有值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 函数和/或 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。

现在我想把这本字典还原成一个图像(字典上的变化)。

【问题讨论】:

  • 这是可行的,但你有什么尝试?
  • @MooingRawr 我没有任何办法去做这件事。
  • 你只有普通的 python 列表...没有 numpy 喜欢
  • @IgnacioVergaraKausel 数组是 numpy 数组。为了简单起见,我在示例中省略了这个细节。
  • 如果你问你应该采取什么方向,那就太自以为是了,太宽泛了。如果您问如何不尝试就做到这一点,那么您是在要求我们为您编写代码,这不是代码编写服务。对不起,如果我说的有点难,但规则就是规则......:\ 显示一些尝试(即使它不正确),我相信你会得到更快的答案。 (注意我没有标记这个,但我想我会等待编辑。)

标签: python arrays numpy dictionary reduce


【解决方案1】:

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

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]])

【讨论】:

    【解决方案2】:

    另一种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 广播功能来实现这一点。

    【讨论】:

      【解决方案3】:

      这是一个 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]])
      

      【讨论】:

        【解决方案4】:

        这可以在没有任何花哨的情况下完成,只需古老的 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)]]
        

        附:也感谢您的努力。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-11-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-07
          • 2017-02-27
          • 2014-06-15
          • 2020-06-18
          • 2015-05-08
          相关资源
          最近更新 更多