web-dev-qa-db-fra.com

copier un tableau 2D dans la 3ème dimension, N fois (Python)

Je voudrais copier un tableau 2D numpy dans une troisième dimension. Par exemple, étant donné le tableau numpy (2D):

import numpy as np
arr = np.array([[1,2],[1,2]])
# arr.shape = (2, 2)

convertissez-le en matrice 3D avec N copies de ce type dans une nouvelle dimension. En agissant sur arr avec N = 3, le résultat devrait être:

new_arr = np.array([[[1,2],[1,2]],[[1,2],[1,2]],[[1,2],[1,2]]])
# new_arr.shape = (3, 2, 2)
65
anon01

Le moyen le plus propre consiste probablement à utiliser np.repeat :

a = np.array([[1, 2], [1, 2]])
print(a.shape)
# (2,  2)

# indexing with np.newaxis inserts a new 3rd dimension, which we then repeat the
# array along, (you can achieve the same effect by indexing with None, see below)
b = np.repeat(a[:, :, np.newaxis], 3, axis=2)

print(b.shape)
# (2, 2, 3)

print(b[:, :, 0])
# [[1 2]
#  [1 2]]

print(b[:, :, 1])
# [[1 2]
#  [1 2]]

print(b[:, :, 2])
# [[1 2]
#  [1 2]]

Ceci dit, vous pouvez souvent éviter de répéter vos tableaux en utilisant broadcast . Par exemple, disons que je voulais ajouter un (3,) vecteur:

c = np.array([1, 2, 3])

à a. Je pouvais copier le contenu de a 3 fois dans la troisième dimension, puis copier le contenu de c deux fois dans la première et la deuxième dimensions, de sorte que mes deux tableaux soient (2, 2, 3), puis calcule leur somme. Cependant, c'est beaucoup plus simple et rapide de faire ceci:

d = a[..., None] + c[None, None, :]

Ici, a[..., None] a la forme (2, 2, 1) et c[None, None, :] a la forme (1, 1, 3) *. Lorsque je calcule la somme, le résultat est "diffusé" selon les dimensions de la taille 1, ce qui me donne un résultat de forme (2, 2, 3):

print(d.shape)
# (2,  2, 3)

print(d[..., 0])    # a + c[0]
# [[2 3]
#  [2 3]]

print(d[..., 1])    # a + c[1]
# [[3 4]
#  [3 4]]

print(d[..., 2])    # a + c[2]
# [[4 5]
#  [4 5]]

La diffusion est une technique très puissante car elle évite les coûts supplémentaires liés à la création de copies répétées de vos tableaux d'entrée en mémoire.


* Bien que je les ai inclus pour plus de clarté, les indices None dans c ne sont pas nécessaires - vous pouvez aussi faire a[..., None] + c, c'est-à-dire diffuser un (2, 2, 1) tableau contre un (3,) tableau. En effet, si l’un des tableaux a moins de dimensions que l’autre, seules les dimensions à la fin des deux tableaux doivent être compatibles. Pour donner un exemple plus compliqué:

a = np.ones((6, 1, 4, 3, 1))  # 6 x 1 x 4 x 3 x 1
b = np.ones((5, 1, 3, 2))     #     5 x 1 x 3 x 2
result = a + b                # 6 x 5 x 4 x 3 x 2
98
ali_m

Une autre façon consiste à utiliser numpy.dstack . Supposons que vous souhaitiez répéter la matrice anum_repeats fois:

import numpy as np
b = np.dstack([a]*num_repeats)

L'astuce consiste à envelopper la matrice a dans une liste d'un seul élément, puis en utilisant le * opérateur pour dupliquer les éléments de cette liste num_repeats fois.

Par exemple, si:

a = np.array([[1, 2], [1, 2]])
num_repeats = 5

Ceci répète le tableau de [1 2; 1 2] 5 fois dans la troisième dimension. Pour vérifier (sous IPython):

In [110]: import numpy as np

In [111]: num_repeats = 5

In [112]: a = np.array([[1, 2], [1, 2]])

In [113]: b = np.dstack([a]*num_repeats)

In [114]: b[:,:,0]
Out[114]: 
array([[1, 2],
       [1, 2]])

In [115]: b[:,:,1]
Out[115]: 
array([[1, 2],
       [1, 2]])

In [116]: b[:,:,2]
Out[116]: 
array([[1, 2],
       [1, 2]])

In [117]: b[:,:,3]
Out[117]: 
array([[1, 2],
       [1, 2]])

In [118]: b[:,:,4]
Out[118]: 
array([[1, 2],
       [1, 2]])

In [119]: b.shape
Out[119]: (2, 2, 5)

À la fin, nous pouvons voir que la forme de la matrice est 2 x 2, avec 5 tranches dans la troisième dimension.

21
rayryeng

Utilisez une vue et obtenez une durée d'exécution gratuite! Étendre les tableaux n-dim Génériques à n+1-dim

Introduit dans NumPy 1.10.0 , nous pouvons utiliser numpy.broadcast_to pour générer simplement une vue 3D Dans le 2D tableau d'entrée. L'avantage serait une surcharge de mémoire et une exécution pratiquement gratuite. Cela serait essentiel dans les cas où les tableaux sont grands et que nous pouvons travailler avec des vues. De plus, cela fonctionnerait avec des cas génériques n-dim.

J'utiliserais le mot stack à la place de copy, car les lecteurs pourraient le confondre avec la copie de tableaux qui créent des copies en mémoire.

Empiler le premier axe

Si nous voulons empiler l'entrée arr le long du premier axe, la solution avec np.broadcast_to Pour créer la vue 3D Serait -

np.broadcast_to(arr,(3,)+arr.shape) # N = 3 here

Empiler le troisième/dernier axe

Pour empiler les entrées arr le long du troisième axe, la solution pour créer la vue 3D Serait -

np.broadcast_to(arr[...,None],arr.shape+(3,))

Si nous avons réellement besoin d’une copie de mémoire, nous pouvons toujours y ajouter .copy(). Par conséquent, les solutions seraient -

np.broadcast_to(arr,(3,)+arr.shape).copy()
np.broadcast_to(arr[...,None],arr.shape+(3,)).copy()

Voici comment fonctionne l'empilement pour les deux cas, avec les informations de forme pour un cas exemple -

# Create a sample input array of shape (4,5)
In [55]: arr = np.random.Rand(4,5)

# Stack along first axis
In [56]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[56]: (3, 4, 5)

# Stack along third axis
In [57]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[57]: (4, 5, 3)

La même solution fonctionnerait pour étendre une entrée n-dim En vue n+1-dim En sortie le long des premier et dernier axes. Examinons quelques cas plus graves -

Cas d’entrée 3D:

In [58]: arr = np.random.Rand(4,5,6)

# Stack along first axis
In [59]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[59]: (3, 4, 5, 6)

# Stack along last axis
In [60]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[60]: (4, 5, 6, 3)

Cas d’entrée 4D:

In [61]: arr = np.random.Rand(4,5,6,7)

# Stack along first axis
In [62]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[62]: (3, 4, 5, 6, 7)

# Stack along last axis
In [63]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[63]: (4, 5, 6, 7, 3)

etc.

Les horaires

Utilisons un exemple volumineux 2D, Obtenons le minutage et vérifions si la sortie est un view.

# Sample input array
In [19]: arr = np.random.Rand(1000,1000)

Prouvons que la solution proposée est bien une vue. Nous utiliserons l'empilement le long du premier axe (les résultats seraient très similaires pour l'empilement le long du troisième axe) -

In [22]: np.shares_memory(arr, np.broadcast_to(arr,(3,)+arr.shape))
Out[22]: True

Faisons en sorte que le minutage montre que c'est pratiquement gratuit -

In [20]: %timeit np.broadcast_to(arr,(3,)+arr.shape)
100000 loops, best of 3: 3.56 µs per loop

In [21]: %timeit np.broadcast_to(arr,(3000,)+arr.shape)
100000 loops, best of 3: 3.51 µs per loop

Étant une vue, augmenter N de 3 À 3000 Ne change rien au minutage et les deux sont négligeables en unités de chronométrage. Par conséquent, efficace à la fois sur la mémoire et la performance!

6
Divakar
A=np.array([[1,2],[3,4]])
B=np.asarray([A]*N)

Editez @ Mr.F, pour préserver l'ordre des dimensions:

B=B.T
3
yevgeniy

Voici un exemple de diffusion qui fait exactement ce qui a été demandé.

a = np.array([[1, 2], [1, 2]])
a=a[:,:,None]
b=np.array([1]*5)[None,None,:]

Alors b*a Est le résultat souhaité et (b*a)[:,:,0] Produit array([[1, 2],[1, 2]]), qui est l'original a, de même que (b*a)[:,:,1], Etc.

2
Mike O'Connor