【问题标题】:How to make a multidimension numpy array with a varying row size?如何制作具有不同行大小的多维 numpy 数组?
【发布时间】:2011-03-24 02:38:49
【问题描述】:

我想创建一个二维 numpy 数组,每行有不同数量的元素。

尝试

cells = numpy.array([[0,1,2,3], [2,3,4]])

报错

ValueError: setting an array element with a sequence.

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    在提出这个问题和你的代码之后,我们现在已经快 7 年了

    cells = numpy.array([[0,1,2,3], [2,3,4]])
    

    在 numpy 1.12.0、python 3.5 中执行,不会产生任何错误并且 cells 包含:

    array([[0, 1, 2, 3], [2, 3, 4]], dtype=object)
    

    您以cells[0][2] # (=2) 的身份访问您的cells 元素。

    如果您想在新元素(即数组)可用时动态构建 numpy 数组列表,则可以使用 append 的替代方法:

    d = []                 # initialize an empty list
    a = np.arange(3)       # array([0, 1, 2])
    d.append(a)            # [array([0, 1, 2])]
    b = np.arange(3,-1,-1) #array([3, 2, 1, 0])
    d.append(b)            #[array([0, 1, 2]), array([3, 2, 1, 0])]
    

    【讨论】:

    • 问题是你仍然不能使用d.mean()、d.flatten()等
    • 这是最理想的方式吗? numpy 数组是否仍然应该是 2D 的统一长度?如果是这样,存储可变长度数据的最佳方式是什么。
    【解决方案2】:

    虽然 Numpy 知道任意对象的数组,但它针对具有固定维度的同构数字数组进行了优化。如果您真的需要数组数组,最好使用嵌套列表。但是根据数据的预期用途,不同的数据结构可能会更好,例如如果您有一些无效的数据点,则为掩码数组。

    如果您真的想要灵活的 Numpy 数组,请使用以下内容:

    numpy.array([[0,1,2,3], [2,3,4]], dtype=object)
    

    但是,这将创建一个存储对列表的引用的一维数组,这意味着您将失去 Numpy 的大部分优势(向量处理、局部性、切片等)。

    【讨论】:

    • numpy“了解”任意对象数组意味着什么?
    • 请记住:关于内存使用:Python 整数列表比 numpy 整数数组效率低得多。我的意思是,不包括列表或数组对象元的少量开销,带有N 整数的列表内容需要(28 + 8) * N 字节,但numpy 需要(8 * N),或者您可以使用np.int16np.int32 等将其减少到2 * N4 * N 字节。 webcourses.ucf.edu/courses/1249560/pages/…
    • @ThammeGowda python 将每个整数存储为 PyObject,因此,它的开销远大于 numpy。
    • @JiaHaoXu 我就是这么说的!我说python的列表比numpy效率低
    • @ThammeGowda,这是关于存储列表,而不是整数。相对于嵌套列表,此对象 dtype 数组没有内存(或速度)优势。 numpy 已经编译了用于以内存和速度有效的方式处理数字 dtype 的代码(尽管制作这样的数组有点费时)。而且像列表一样在数组上进行迭代会更慢。
    【解决方案3】:

    这在 Numpy 中没有得到很好的支持(根据定义,几乎在任何地方,“二维数组”的所有行都具有相同的长度)。 Numpy 数组的 Python 列表可能对您来说是一个很好的解决方案,因为这样您就可以在可以使用它们的地方获得 Numpy 的优势:

    cells = [numpy.array(a) for a in [[0,1,2,3], [2,3,4]]]
    

    【讨论】:

      【解决方案4】:

      另一种选择是将您的数组存储为一个连续的数组,并存储它们的大小或偏移量。这需要更多关于如何操作数组的概念性思考,但是可以使大量的操作像您有一个具有不同大小的二维数组一样工作。在他们不能的情况下,np.split 可用于创建 calocedrus 推荐的列表。最简单的操作是 ufunc,因为它们几乎不需要修改。以下是一些示例:

      cells_flat = numpy.array([0, 1, 2, 3, 2, 3, 4])
      # One of these is required, it's pretty easy to convert between them,
      # but having both makes the examples easy
      cell_lengths = numpy.array([4, 3])
      cell_starts = numpy.insert(cell_lengths[:-1].cumsum(), 0, 0)
      cell_lengths2 = numpy.diff(numpy.append(cell_starts, cells_flat.size))
      assert np.all(cell_lengths == cell_lengths2)
      
      # Copy prevents shared memory
      cells = numpy.split(cells_flat.copy(), cell_starts[1:])
      # [array([0, 1, 2, 3]), array([2, 3, 4])]
      
      numpy.array([x.sum() for x in cells])
      # array([6, 9])
      numpy.add.reduceat(cells_flat, cell_starts)
      # array([6, 9])
      
      [a + v for a, v in zip(cells, [1, 3])]
      # [array([1, 2, 3, 4]), array([5, 6, 7])]
      cells_flat + numpy.repeat([1, 3], cell_lengths)
      # array([1, 2, 3, 4, 5, 6, 7])
      
      [a.astype(float) / a.sum() for a in cells]
      # [array([ 0.        ,  0.16666667,  0.33333333,  0.5       ]),
      #  array([ 0.22222222,  0.33333333,  0.44444444])]
      cells_flat.astype(float) / np.add.reduceat(cells_flat, cell_starts).repeat(cell_lengths)
      # array([ 0.        ,  0.16666667,  0.33333333,  0.5       ,  0.22222222,
      #         0.33333333,  0.44444444])
      
      def complex_modify(array):
          """Some complicated function that modifies array
      
          pretend this is more complex than it is"""
          array *= 3
      
      for arr in cells:
          complex_modify(arr)
      cells
      # [array([0, 3, 6, 9]), array([ 6,  9, 12])]
      for arr in numpy.split(cells_flat, cell_starts[1:]):
          complex_modify(arr)
      cells_flat
      # array([ 0,  3,  6,  9,  6,  9, 12])
      

      【讨论】:

      • 这是一个很好的解决方案。巧妙地使用reduceat。 +1
      【解决方案5】:

      在 numpy 1.14.3 中,使用追加:

      d = []                 # initialize an empty list
      a = np.arange(3)       # array([0, 1, 2])
      d.append(a)            # [array([0, 1, 2])]
      b = np.arange(3,-1,-1) #array([3, 2, 1, 0])
      d.append(b)            #[array([0, 1, 2]), array([3, 2, 1, 0])]
      

      你会得到一个数组列表(可以是不同长度的),你可以执行d[0].mean() 之类的操作。另一方面,

      cells = numpy.array([[0,1,2,3], [2,3,4]])
      

      产生一个列表数组。

      您可能想要这样做:

      a1 = np.array([1,2,3])
      a2 = np.array([3,4])
      a3 = np.array([a1,a2])
      a3 # array([array([1, 2, 3]), array([3, 4])], dtype=object)
      type(a3) # numpy.ndarray
      type(a2) # numpy.ndarray
      

      【讨论】:

        【解决方案6】:

        有点跑题了,但没有人们想象的那么多,因为急切模式现在是默认模式: 如果你使用的是 Tensorflow,你可以这样做:

        a = tf.ragged.constant([[0, 1, 2, 3]])
        b = tf.ragged.constant([[2, 3, 4]])
        c = tf.concat([a, b], axis=0)
        

        然后您仍然可以进行所有数学运算,例如tf.math.reduce_mean 等。

        【讨论】:

          【解决方案7】:

          np.array([[0,1,2,3], [2,3,4]], dtype=object) 返回列表的“数组”。

          a = np.array([np.array([0,1,2,3]), np.array([2,3,4])], dtype=object) 返回一个数组数组。它已经允许诸如a+1 之类的操作。

          在此基础上,可以通过子类化来增强功能。

          import numpy as np
          
          class Arrays(np.ndarray):
              def __new__(cls, input_array, dims=None):
                  obj = np.array(list(map(np.array, input_array))).view(cls)
                  return obj
              def __getitem__(self, ij):
                  if isinstance(ij, tuple) and len(ij) > 1:
                      # handle twodimensional slicing
                      if isinstance(ij[0],slice) or hasattr(ij[0], '__iter__'):
                          # [1:4,:] or [[1,2,3],[1,2]]
                          return Arrays(arr[ij[1]] for arr in self[ij[0]])
                      return self[ij[0]][ij[1]] # [1,:] np.array
                  return super(Arrays, self).__getitem__(ij)
              def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
                  axis = kwargs.pop('axis', None)
                  dimk = [len(arg) if hasattr(arg, '__iter__') else 1 for arg in inputs]
                  dim = max(dimk)
                  pad_inputs = [([i]*dim if (d<dim) else i) for d,i in zip(dimk, inputs)]
                  result = [np.ndarray.__array_ufunc__(self, ufunc, method, *x, **kwargs) for x in zip(*pad_inputs)]
                  if method == 'reduce':
                      # handle sum, min, max, etc.
                      if axis == 1:
                          return np.array(result)
                      else:
                          # repeat over remaining axis
                          return np.ndarray.__array_ufunc__(self, ufunc, method, result, **kwargs)
                  return Arrays(result)
          

          现在可以了:

          a = Arrays([[0,1,2,3], [2,3,4]])
          a[0:1,0:-1]
          # Arrays([[0, 1, 2]])
          np.sin(a)
          # Arrays([array([0.        , 0.84147098, 0.90929743, 0.14112001]),
          #        array([ 0.90929743,  0.14112001, -0.7568025 ])], dtype=object)
          a + 2*a
          # Arrays([array([0, 3, 6, 9]), array([ 6,  9, 12])], dtype=object)
          

          要让 nanfunctions 工作,可以这样做

          # patch for nanfunction that cannot handle the object-ndarrays along with second axis=-1
          def nanpatch(func):
              def wrapper(a, axis=None, **kwargs):
                  if isinstance(a, Arrays):
                      rowresult = [func(x, **kwargs) for x in a]
                      if axis == 1:
                          return np.array(rowresult)
                      else:
                          # repeat over remaining axis
                          return func(rowresult)
                  # otherwise keep the original version
                  return func(a, axis=axis, **kwargs)
              return wrapper
          
          np.nanmean = nanpatch(np.nanmean)
          np.nansum = nanpatch(np.nansum)
          np.nanmin = nanpatch(np.nanmin)
          np.nanmax = nanpatch(np.nanmax)
          
          np.nansum(a)
          # 15
          np.nansum(a, axis=1)
          # array([6, 9])
          

          【讨论】:

            猜你喜欢
            • 2017-11-04
            • 1970-01-01
            • 2013-06-18
            • 2018-07-12
            • 2017-12-23
            • 1970-01-01
            • 1970-01-01
            • 2011-07-30
            相关资源
            最近更新 更多