连接Numpy数组而不进行复制


75

在Numpy中,我可以使用np.appendor端对端连接两个数组np.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:我有一长串要在其上执行单个全局标准化的数组。
弗雷德·富

列表理解也将很快。
电子人

那没有回答问题,复制所有这些数组怎么了?基本上,您是否担心复制成本,还是要修改原始阵列?
温斯顿·埃韦特

1
@WinstonEwert:复制的成本是问题所在;否则,我只能使用concatenate它们,并用连接视图替换原始数组。看起来这就是我要做的。
弗雷德·富

Answers:


78

属于Numpy数组的内存必须是连续的。如果单独分配数组,它们将随机分散在内存中,并且无法将它们表示为View Numpy数组。

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


12
无关紧要的注释:视图的内存不必是连续的,但可能必须以固定的步幅进行排序(数组列表也不是这样)。
靠机械装置

您是在说一个子类都行不通吗?我知道人们使用ndarray子类来处理mmap'd数组,但是我想内存映射也是连续的……
Fred Foo

3
是的,子类还必须遵守Numpy的内存模型。(以上@cyborgs的评论也是正确的:子数组也可以以固定的步幅在内存中排序,但也只能通过事先安排事物才能获得。)仔细阅读此页可能会启发您更多注意。
光伏。

1
您是否建议切片,是不是有原因numpy.empty
lucidbrot

12

只需初始化数组,然后再将其填充数据即可。如果需要,您可以分配超出需要的空间,并且由于numpy的工作方式,它不会占用更多的RAM。

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

仅在将数据放入阵列后才使用内存。从两个连接创建一个新数组永远不会在任何大小的数据集上完成,例如,数据集> 1GB左右。


2

根本不是很优雅,但是您可以使用元组来存储指向数组的指针,从而接近所需的内容。现在我不知道该如何在案件中使用它,但我之前已经做过类似的事情。

>>> 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索引魔术。谢谢你
弗雷德·富

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)

0

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

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

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


0

答案基于我在对ndarray中的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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.