Utilisez une vue et obtenez une exécution gratuite! Étendre les n-dim
baies génériques àn+1-dim
Introduit dans NumPy1.10.0
, nous pouvons utiliser numpy.broadcast_to
pour générer simplement une 3D
vue dans le 2D
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 des n-dim
cas génériques .
J'utiliserais le mot stack
à la place de copy
, 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 avec np.broadcast_to
pour créer une 3D
vue serait -
np.broadcast_to(arr,(3,)+arr.shape) # N = 3 here
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 une 3D
vue serait -
np.broadcast_to(arr[...,None],arr.shape+(3,))
Si nous avons réellement besoin d'une copie mémoire, nous pouvons toujours .copy()
y ajouter . 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, montré avec leurs informations de forme pour un exemple de cas -
# 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)
Les mêmes solutions fonctionneraient pour étendre une n-dim
entrée pour n+1-dim
afficher la sortie le long des premier et dernier axes. Explorons quelques cas plus faibles -
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.
Timings
Utilisons un grand exemple de 2D
cas, obtenons les horaires et vérifions que 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
Prenons les horaires pour montrer 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
Être une vue, passer N
de 3
à 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!
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)
.