86 votes

Concaténer des tableaux Numpy sans les copier

Dans Numpy, je peux concaténer deux tableaux bout à bout avec np.append o 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 ces derniers 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]])

Existe-t-il un moyen de concaténer deux tableaux en un seul ? vue c'est-à-dire sans copie ? Cela nécessiterait-il un np.ndarray sous-classe ?

0 votes

Pourquoi voulez-vous avoir une vue plutôt qu'une copie ?

3 votes

@WinstonEwert : J'ai une longue liste de tableaux sur lesquels je veux effectuer une normalisation unique et globale.

0 votes

La compréhension de la liste sera également rapide.

94voto

pv. Points 9935

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

Si vous connaissez à l'avance le nombre de tableaux dont vous avez besoin, vous pouvez commencer par un grand tableau que vous allouez à l'avance, et faire en sorte que chacun des petits tableaux soit une vue sur le grand tableau (par exemple, obtenue par découpage).

14 votes

Remarque sans importance : la mémoire d'une vue n'a pas besoin d'être contiguë, mais elle doit probablement être ordonnée selon des pas fixes (ce qui n'est pas non plus le cas d'une liste de tableaux).

0 votes

Êtes-vous en train de dire que même une sous-classe ne fonctionnera pas ? Je sais que des gens utilisent ndarray sous-classes pour travailler avec mmap Mais je suppose que les mappages de mémoire sont également contigus...

4 votes

Oui, les sous-classes doivent également adhérer au modèle de mémoire de Numpy. (Le commentaire de @cyborgs ci-dessus est également correct : les sous-réseaux pourraient également être ordonnés en mémoire avec des strides fixes, mais cela ne peut être obtenu qu'en arrangeant les choses à l'avance). Une lecture attentive de cette page peut apporter un peu plus de lumière.

16voto

John Points 349

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 de la façon dont numpy fonctionne.

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

La mémoire n'est utilisée qu'une fois que les données sont placées dans le tableau. La création d'un nouveau tableau à partir de la concaténation de deux tableaux n'aboutira jamais sur un ensemble de données de quelque taille que ce soit, c'est-à-dire un ensemble de données > 1 Go environ.

1voto

Brian Larsen Points 619

Ce n'est pas très élégant, mais on peut se rapprocher de ce que l'on veut en utilisant un tuple pour stocker les pointeurs vers les tableaux. Je n'ai aucune idée de la façon dont je l'utiliserais dans ce 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]])

0 votes

Oui, mais cela ne me donnera pas le genre de magie d'indexation NumPy que j'aimerais avoir. Merci en tout cas.

1voto

architectonic Points 1

J'ai eu le même problème et j'ai fini par le faire à l'envers, après avoir concaténé normalement (avec copie) j'ai réassigné les tableaux originaux 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)

0voto

e.tadeu Points 1505

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

>>> 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 qu'il crée des copies lors des opérations de diffusion (ce qui ressemble à un bogue).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X