Le moyen le plus propre est probablement d'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]]
Cela dit, vous pouvez souvent éviter de répéter complètement vos tableaux en utilisant la diffusion . Par exemple, disons que je voulais ajouter un (3,)
vecteur:
c = np.array([1, 2, 3])
à a
. Je pourrais copier le contenu de a
3 fois dans la troisième dimension, puis copier le contenu de c
deux fois dans les première et deuxième dimensions, de sorte que mes deux tableaux soient (2, 2, 3)
, puis calculer leur somme. Cependant, c'est beaucoup plus simple et rapide de le faire:
d = a[..., None] + c[None, None, :]
Ici, a[..., None]
a forme (2, 2, 1)
et c[None, None, :]
a forme (1, 1, 3)
*. Lorsque je calcule la somme, le résultat est `` diffusé '' le long des dimensions de taille 1, me donnant 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 la surcharge supplémentaire liée à 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 None
index dans c
ne sont pas réellement nécessaires - vous pouvez également le faire a[..., None] + c
, c'est-à-dire diffuser un (2, 2, 1)
tableau sur un (3,)
tableau. En effet , si l' un des réseaux a moins de dimensions que l'autre alors que les arrière dimensions des deux réseaux 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
b[:,:,0]
,b[:,:,1]
etb[:,:,2]
. Chaque tranche de troisième dimension est une copie du tableau 2D d'origine. Ce n'est pas aussi évident à regarderprint(b)
.np.newaxis
est juste un alias deNone
Une autre façon est d'utiliser
numpy.dstack
. Supposons que vous souhaitiez répéter lesa
num_repeats
heures de la matrice :L'astuce consiste à envelopper la matrice
a
dans une liste d'un seul élément, puis à utiliser l'*
opérateur pour dupliquer les éléments dans cette listenum_repeats
fois.Par exemple, si:
Cela répète le tableau de
[1 2; 1 2]
5 fois dans la troisième dimension. Pour vérifier (dans IPython):À la fin, nous pouvons voir que la forme de la matrice est
2 x 2
, avec 5 tranches dans la troisième dimension.la source
reshape
t-il? Plus rapide? donne la même structure? C'est vraiment plus soigné.Utilisez une vue et obtenez une exécution gratuite! Étendre les
n-dim
baies génériques àn+1-dim
Introduit dans NumPy
1.10.0
, nous pouvons utilisernumpy.broadcast_to
pour générer simplement une3D
vue dans le2D
tableau d'entrée. L'avantage serait aucune surcharge de mémoire supplémentaire et une durée d'exécution pratiquement gratuite. Ce serait essentiel dans les cas où les tableaux sont grands et où nous pouvons travailler avec des vues. En outre, cela fonctionnerait avec desn-dim
cas génériques .J'utiliserais le mot
stack
à la place decopy
, car les lecteurs pourraient le confondre avec la copie de tableaux qui crée des copies de mémoire.Empiler le long du premier axe
Si nous voulons empiler les entrées le
arr
long du premier axe, la solution avecnp.broadcast_to
pour créer une3D
vue serait -Empiler le long du troisième / dernier axe
Pour empiler les entrées le
arr
long du troisième axe, la solution pour créer une3D
vue serait -Si nous avons réellement besoin d'une copie mémoire, nous pouvons toujours
.copy()
y ajouter . Par conséquent, les solutions seraient -Voici comment fonctionne l'empilement pour les deux cas, montré avec leurs informations de forme pour un exemple de cas -
Les mêmes solutions fonctionneraient pour étendre une
n-dim
entrée pourn+1-dim
afficher la sortie le long des premier et dernier axes. Explorons quelques cas plus faibles -Cas d'entrée 3D:
Cas d'entrée 4D:
etc.
Timings
Utilisons un grand exemple de
2D
cas, obtenons les horaires et vérifions que la sortie est unview
.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) -
Prenons les horaires pour montrer que c'est pratiquement gratuit -
Être une vue, passer
N
de3
à3000
rien changé sur les timings et les deux sont négligeables sur les unités de chronométrage. Par conséquent, efficace à la fois sur la mémoire et les performances!la source
Modifier @ Mr.F, pour conserver l'ordre des dimensions:
la source
B.shape
imprime(N, 2, 2)
pour n'importe quelle valeur deN
. Si vous transposezB
avec,B.T
il correspond à la sortie attendue.B[0], B[1],...
qui vous donnera la bonne tranche, ce que je soutiendrai et dirai que c'est plus facile à taper qu'à utiliserB[:,:,0], B[:,:,1]
, etc.B[:,:,i]
aussi bien que c'est ce à quoi je suis habitué.Voici un exemple de diffusion qui fait exactement ce qui a été demandé.
Puis
b*a
est le résultat souhaité et(b*a)[:,:,0]
produitarray([[1, 2],[1, 2]])
, qui est l'originala
, comme le fait(b*a)[:,:,1]
, etc.la source
Cela peut maintenant également être réalisé en utilisant np.tile comme suit:
la source