【问题标题】:Concatenate Numpy arrays without copying连接 Numpy 数组而不复制
【发布时间】:2011-12-13 17:19:52
【问题描述】:

在 Numpy 中,我可以使用 np.appendnp.concatenate 端到端连接两个数组:

>>> X = np.array([[1,2,3]])
>>> Y = np.array([[-1,-2,-3],[4,5,6]])
>>> Z = np.append(X, Y, axis=0)
>>> Z
array([[ 1,  2,  3],
       [-1, -2, -3],
       [ 4,  5,  6]])

但是这些会复制他们的输入数组:

>>> Z[0,:] = 0
>>> Z
array([[ 0,  0,  0],
       [-1, -2, -3],
       [ 4,  5,  6]])
>>> X
array([[1, 2, 3]])

有没有办法将两个数组连接成一个视图,即不复制?这需要np.ndarray 子类吗?

【问题讨论】:

  • 为什么要查看而不是副本?
  • @WinstonEwert:我有一长串数组,我想在这些数组上执行单一的全局规范化。
  • 列表理解也会很快。
  • 这并不能回答问题,复制所有这些数组有什么问题?基本上,你是担心复制的成本,还是想修改原始数组?
  • @WinstonEwert:复制的成本是问题;否则我可以concatenate 他们并将原始数组替换为连接中的视图。不过,看起来这就是我必须做的。

标签: python multidimensional-array numpy


【解决方案1】:

我遇到了同样的问题并最终将其反转,在正常连接后(使用副本)我重新分配了原始数组以成为连接的视图:

import numpy as np

def concat_no_copy(arrays):
    """ Concats the arrays and returns the concatenated array 
    in addition to the original arrays as views of the concatenated one.

    Parameters:
    -----------
    arrays: list
        the list of arrays to concatenate
    """
    con = np.concatenate(arrays)

    viewarrays = []
    for i, arr in enumerate(arrays):
        arrnew = con[sum(len(a) for a in arrays[:i]):
                     sum(len(a) for a in arrays[:i + 1])]
        viewarrays.append(arrnew)
        assert all(arr == arrnew)

    # return the view arrays, replace the old ones with these
    return con, viewarrays

你可以如下测试:

def test_concat_no_copy():
    arr1 = np.array([0, 1, 2, 3, 4])
    arr2 = np.array([5, 6, 7, 8, 9])
    arr3 = np.array([10, 11, 12, 13, 14])

    arraylist = [arr1, arr2, arr3]

    con, newarraylist = concat_no_copy(arraylist)

    assert all(con == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
                                11, 12, 13, 14]))

    for old, new in zip(arraylist, newarraylist):
        assert all(old == new)

【讨论】:

    【解决方案2】:

    答案基于我在Reference to ndarray rows in ndarray的另一个答案

    X = np.array([[1,2,3]])
    Y = np.array([[-1,-2,-3],[4,5,6]])
    Z = np.array([None, None, None])
    Z[0] = X[0]
    Z[1] = Y[0]
    Z[2] = Y[1]
    
    Z[0][0] = 5 # X would be changed as well
    
    print(X)
    Output: 
    array([[5, 2, 3]])
    
    # Let's make it a function!
    def concat(X, Y, copy=True):
        """Return an array of references if copy=False""" 
        if copy is True:  # deep copy
            return np.append(X, Y, axis=0)
        len_x, len_y = len(X), len(Y)
        ret = np.array([None for _ in range(len_x + len_y)])
        for i in range(len_x):
            ret[i] = X[i]
        for j in range(len_y):
            ret[len_x + j] = Y[j] 
        return ret
    

    【讨论】:

      【解决方案3】:

      只需在填充数据之前初始化数组。如果您愿意,您可以分配比需要更多的空间,并且由于 numpy 的工作方式,它不会占用更多的 RAM。

      A = np.zeros(R,C)
      A[row] = [data]
      

      只有将数据放入数组时才会使用内存。在任何大小的数据集(即数据集 > 1GB 左右)上,从两个连接创建一个新数组永远不会完成。

      【讨论】:

        【解决方案4】:

        您可以创建一个数组数组,例如:

        >>> from numpy import *
        >>> a = array([1.0, 2.0, 3.0])
        >>> b = array([4.0, 5.0])
        >>> c = array([a, b])
        >>> c
        array([[ 1.  2.  3.], [ 4.  5.]], dtype=object)
        >>> a[0] = 100.0
        >>> a
        array([ 100.,    2.,    3.])
        >>> c
        array([[ 100.    2.    3.], [ 4.  5.]], dtype=object)
        >>> c[0][1] = 200.0
        >>> a
        array([ 100.,  200.,    3.])
        >>> c
        array([[ 100.  200.    3.], [ 4.  5.]], dtype=object)
        >>> c *= 1000
        >>> c
        array([[ 100000.  200000.    3000.], [ 4000.  5000.]], dtype=object)
        >>> a
        array([ 100.,  200.,    3.])
        >>> # Oops! Copies were made...
        

        问题在于它会在广播操作中创建副本(听起来像是一个错误)。

        【讨论】:

          【解决方案5】:

          一点也不优雅,但您可以使用元组来存储指向数组的指针,从而接近您想要的。现在我不知道如何在这种情况下使用它,但我以前做过这样的事情。

          >>> X = np.array([[1,2,3]])
          >>> Y = np.array([[-1,-2,-3],[4,5,6]])
          >>> z = (X, Y)
          >>> z[0][:] = 0
          >>> z
          (array([[0, 0, 0]]), array([[-1, -2, -3],
                 [ 4,  5,  6]]))
          >>> X
          array([[0, 0, 0]])
          

          【讨论】:

          • 是的,但这不会给我我想要的那种 NumPy 索引魔法。无论如何,谢谢。
          【解决方案6】:

          属于 Numpy 数组的内存必须是连续的。如果单独分配数组,它们会随机分散在内存中,没有办法将它们表示为视图 Numpy 数组。

          如果您事先知道需要多少个数组,则可以改为从预先分配的一个大数组开始,并让每个小数组成为大数组的视图(例如通过切片获得)。

          【讨论】:

          • 无关紧要的备注:视图的内存不必是连续的,但它可能必须以固定的步幅排序(数组列表也不是这种情况)。
          • 你是说即使是子类也行不通?我知道人们使用ndarray 子类来处理mmap'd 数组,但我猜内存映射也是连续的......
          • 是的,子类也必须遵守 Numpy 的内存模型。 (@cyborgs 上面的评论也是正确的:子数组也可以在内存中以固定的步幅排序,但也只能通过事先安排来获得。)仔细阅读this page 可能会更清楚。跨度>
          • 您是否有理由建议切片,而不是例如numpy.empty?
          猜你喜欢
          • 2011-07-30
          • 2020-09-29
          • 2014-07-04
          • 1970-01-01
          • 2016-08-19
          • 2015-07-12
          • 2016-03-22
          相关资源
          最近更新 更多