Объединение массивов Numpy без копирования


в Numpy, я могу объединить два массива конец в конец с np.append или 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]])

есть ли способ объединить два массива в view, т. е. без копирования? Это потребует np.ndarray подкласс?

6 53

6 ответов:

память, принадлежащая массиву Numpy, должна быть непрерывной. Если вы выделили массивы отдельно, они случайным образом разбросаны в памяти, и нет никакого способа представить их в виде массива numpy.

Если вы заранее знаете, сколько массивов вам нужно, вы можете вместо этого начать с одного большого массива, который вы выделяете заранее, и каждый из небольших массивов будет видом на большой массив (например, полученный путем нарезки).

просто инициализируйте массив, прежде чем заполнять его данными. Если вы хотите, вы можете выделить больше места, чем необходимо, и это не займет больше оперативной памяти из-за того, как работает numpy.

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

память используется только один раз данные помещаются в массив. Создание нового массива из объединения двух никогда не закончится на наборе данных любого размера, т. е. dataset > 1GB или около того.

не очень элегантно, но вы можете приблизиться к тому, что вы хотите, используя Кортеж для хранения указателей на массивы. Теперь я понятия не имею, как бы я использовал его в этом случае, но я делал такие вещи раньше.

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

вы можете создать массив массивов, как:

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

проблема в том, что он создает копии на вещательных операций (похоже на баг).

ответ основан на моем другом ответе в ссылка на строки 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

у меня была та же проблема, и в конечном итоге она была отменена, после конкатенации обычно (с копией) я переназначил исходные массивы, чтобы стать представлениями на конкатенированном:

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)