【问题标题】:Changing shape of a numpy array in a way that keeps the indices/positions of elements the same以保持元素索引/位置相同的方式更改 numpy 数组的形状
【发布时间】:2021-08-16 21:34:18
【问题描述】:

假设我有以下 numpy 数组

[[[1 2 3]
  [4 5 6]
  [7 8 9]]

 [[1 2 3]
  [4 5 6]
  [7 8 9]]

 [[1 2 3]
  [4 5 6]
  [7 8 9]]]

我希望能够调整此数组的大小(使其沿轴变小或变大),但现有元素具有与调整大小之前相同的索引。因此,如果我将轴 2 的大小减少一个元素,它会如下所示:

[[[1 2]
  [4 5]
  [7 8]]

 [[1 2]
  [4 5]
  [7 8]]

 [[1 2]
  [4 5]
  [7 8]]]

如果我增加轴 1 的大小,它会是这样的:

[[[1 2 3]
  [4 5 6]
  [7 8 9]
  [0 0 0]]

 [[1 2 3]
  [4 5 6]
  [7 8 9]
  [0 0 0]]

 [[1 2 3]
  [4 5 6]
  [7 8 9]
  [0 0 0]]]

如果不自己实现所有循环和所有内容,我该怎么做?


作为参考,如果我使用 Numpy resize() 函数,并执行 np.resize(my_array, (3, 3, 2)) 将轴 2 的大小从 3 减小到 2,Numpy 只会更改维度的大小并且不会重新组织数组数据本身,这意味着不保留元素的索引:

[[[1 2]
  [3 4]
  [5 6]]

 [[7 8]
  [9 1]
  [2 3]]

 [[4 5]
  [6 7]
  [8 9]]]

【问题讨论】:

  • 看起来像是一个(或两个)连接作业,添加了新的列和行。

标签: python arrays numpy


【解决方案1】:

您可以尝试定义一个继承自 np.ndarray 类的类,定义一个额外的绑定函数来执行“调整大小”:

import numpy as np

class Array(np.ndarray):
    def __new__(cls, a, dtype=None, order=None):
        obj = np.asarray(a, dtype, order).view(cls)
        return obj
    
    def __array_wrap__(self, out_arr, context=None):
        return np.ndarray.__array_wrap__(self, out_arr, context)

    def resizing(self, cols):
        return self[..., :cols]

arr = np.tile(np.arange(1, 10), 3).reshape(3, 3, 3)
arr = Array(arr)

现在我们可以这样做了:

print(arr.resizing(1))

输出:

[[[1]
  [4]
  [7]]

 [[1]
  [4]
  [7]]

 [[1]
  [4]
  [7]]]
print(arr.resizing(2))

输出:

[[[1 2]
  [4 5]
  [7 8]]

 [[1 2]
  [4 5]
  [7 8]]

 [[1 2]
  [4 5]
  [7 8]]]

【讨论】:

    【解决方案2】:

    你不想要resizereshape

    制作一个样本数组:

    In [29]: arr = np.arange(1,10).reshape(1,3,3)
    In [30]: arr
    Out[30]: 
    array([[[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]]])
    

    要获取 (3,3,3) 数组,请使用 repeat。为了让答案简短,我将坚持使用 (1,3,3):

    In [31]: arr.repeat(3,0)
    Out[31]: 
    array([[[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]],
    
           [[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]],
    
           [[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]]])
    

    切片可以“切断”一列

    In [33]: arr[:,:,:2]
    Out[33]: 
    array([[[1, 2],
            [4, 5],
            [7, 8]]])
    

    添加一行,使用concatenate:

    In [36]: np.concatenate((arr, np.zeros((1,1,3),int)), axis=1)
    Out[36]: 
    array([[[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9],
            [0, 0, 0]]])
    

    请记住,numpy 将数组存储为 shape 和一维数据缓冲区。

    In [40]: arr.ravel()
    Out[40]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])
    

    arr 是来自arangereshape。重塑保留该一维源。所以它不适合你想要的那种改变。 resize 也适用于该一维源,尽管它可以添加或减去它。考虑使用从原始数组中选择的值创建一个新数组,而不是更改数组。

    【讨论】:

      【解决方案3】:

      我会使用np.padnp.zeros

      1. 使用 np.pad:

      构建示例数组:

      >>> import numpy as np               
      >>> 
      >>> A = np.resize(np.r_[1:10],(3,3,3))
      >>> A
      array([[[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]],
      
             [[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]],
      
             [[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]]])
      

      定义函数

      >>> def recut_pad(A,shp):
      ...     return np.pad(A[tuple(map(slice,shp))],[(0,max(0,sn-so)) for sn,so in zip(shp,A.shape)])
      

      试试看:

      >>> recut_pad(A,(2,4,5))
      array([[[1, 2, 3, 0, 0],
              [4, 5, 6, 0, 0],
              [7, 8, 9, 0, 0],
              [0, 0, 0, 0, 0]],
      
             [[1, 2, 3, 0, 0],
              [4, 5, 6, 0, 0],
              [7, 8, 9, 0, 0],
              [0, 0, 0, 0, 0]]])
      >>> recut_pad(A,(4,2,2))
      array([[[1, 2],
              [4, 5]],
      
             [[1, 2],
              [4, 5]],
      
             [[1, 2],
              [4, 5]],
      
             [[0, 0],
              [0, 0]]])
      
      1. 使用np.zeros 和切片:

      定义函数:

      >>> def recut_zeros(A,shp):                                           
      ...     out = np.zeros(shp,A.dtype)                              
      ...     out[tuple(map(slice,A.shape))] = A[tuple(map(slice,shp))]
      ...     return out
      

      验证:

      >>> np.all(recut_pad(A,(1,5,4))==recut_zeros(A,(1,5,4)))
      True
      >>> np.all(recut_pad(A,(7,2,3))==recut_zeros(A,(7,2,3)))
      True
      

      【讨论】:

        猜你喜欢
        • 2021-04-04
        • 2022-01-08
        • 1970-01-01
        • 2023-01-07
        • 2018-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-08
        相关资源
        最近更新 更多