web-dev-qa-db-fra.com

Concaténer les tableaux Numpy sans les copier

Dans Numpy, je peux concaténer deux tableaux de bout en bout avec np.append ou 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]])

Mais ceux-ci font des copies de leurs tableaux d'entrée:

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

Y at-il un moyen de concaténer deux tableaux dans une vue , c’est-à-dire sans copier? Cela nécessiterait-il une sous-classe np.ndarray?

57
Fred Foo

La mémoire appartenant à un tableau Numpy doit être contiguë. Si vous allouez les tableaux séparément, ils sont dispersés de manière aléatoire dans la mémoire et il n’ya aucun moyen de les représenter sous forme de tableau Numpy.

Si vous connaissez à l'avance le nombre de tableaux dont vous avez besoin, vous pouvez commencer avec un grand tableau que vous avez alloué auparavant et que chacun des petits tableaux soit une vue du grand tableau (obtenu par exemple par découpage).

59
pv.

Il suffit d’initialiser le tableau avant de le remplir avec des données. Si vous le souhaitez, vous pouvez allouer plus d’espace que nécessaire et cela ne prendra pas plus de RAM en raison du fonctionnement de numpy.

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

La mémoire n'est utilisée qu'une fois les données placées dans le tableau. Créer un nouveau tableau à partir de la concaténation de deux ne se terminera jamais sur un jeu de données de toute taille, c'est-à-dire> 1 Go ou plus.

6
John

Pas vraiment élégant du tout, mais vous pouvez vous rapprocher de ce que vous voulez en utilisant un Tuple pour stocker des pointeurs sur les tableaux. Maintenant, je n'ai aucune idée de la façon dont je l'utiliserais dans le cas mais j'ai déjà fait des choses comme ça auparavant.

>>> 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]])
1
Brian Larsen

J'ai eu le même problème et j'ai fini par l'inverser, après avoir concaténé normalement (avec copie), j'ai réassigné les tableaux d'origine pour qu'ils deviennent des vues sur le tableau concaténé:

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

Vous pouvez le tester comme suit:

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
architectonic

La réponse est basée sur mon autre réponse dans Référence aux lignes ndarray dans 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
0
Tai

Vous pouvez créer un tableau de tableaux, comme:

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

Le problème est que cela crée des copies sur les opérations de diffusion (cela ressemble à un bogue).

0
e.tadeu