Please consider the more readable HTML version of this document
Array reshaping and rotation can be confusing. in this short guide I will try to visualize what happens when using the numpy functions corresponding to these operations.
from IPython.display import HTML
import numpy as np
from html_3d_array import asvolume# Skip this cell.
# Build an array for this tutorial.
l=['♥','♣','♦','♠']
# l=[x for x in l]
p=np.vstack((l,l,l))
a=np.stack((p,p),axis=1)# The tutorial starts with this 3D array
# 3D means it has 3 dimensions.
print(a)
print("shape:", a.shape)
print("dimensions:", len(a.shape))
# We can think o[[['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']]
[['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']]
[['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']]]
shape: (3, 2, 4)
dimensions: 3
# reshape(...) gives a new shape to an array without changing its data.
# The array dta is a flat sequence of characters registered somewhere in memory
data = a.flatten()
print("data:", ''.join(data))data: ♥♣♦♠♥♣♦♠♥♣♦♠♥♣♦♠♥♣♦♠♥♣♦♠
An array can be reshaped only if the product of the dimensions is the same. for example:
- (3,2,4) (3,4,2) (2,3,4) (2,3,4) (4,2,3) (4,3,2)
are all valid shapes
# let's do a reshape manually
# new shape
i=2
j=3
k=4
# print the data in rows according to the new shape
for z in range(i):
for y in range(j):
for x in range(k):
# take a value from the data
v=data[z*j*k + y*k + x]
print(v, end='')
print()
print()
# this is analougous to
print(a.reshape(i,j,k))
HTML(asvolume(a.reshape(i,j,k)))
# and the same result can be obtained with np.moveaxis(a, 0, 1)♥♣♦♠
♥♣♦♠
♥♣♦♠
♥♣♦♠
♥♣♦♠
♥♣♦♠
[[['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']]
[['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']]]
| ♥ | ♣ | ♦ | ♠ | |
| ♥ | ♥ | ♣ | ♦ | ♠ |
| ♥ | ♥ | ♣ | ♦ | ♠ |
| ♥ | ♣ | ♦ | ♠ |
What reshape does is just "cut the array data according to the values ijk".
Take your time to learn to visualize 3D arrays as volumes.
The following volume has
- the page index increases from first to last
- the row index increases from top to bottom (in the page)
- the column index increases from left to right (in the page)
print(a)
HTML(asvolume(a))[[['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']]
[['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']]
[['♥' '♣' '♦' '♠']
['♥' '♣' '♦' '♠']]]
| ♥ | ♣ | ♦ | ♠ | ||
| ♥ | ♥ | ♣ | ♦ | ♠ | |
| ♥ | ♥ | ♣ | ♦ | ♠ | |
| ♥ | ♣ | ♦ | ♠ |
This array has 3 dimensions:
-
$ijk$ ,$ikj$ ,$jik$ ,$jki$ ,$kij$ ,$kji$
In 3D it can be seen as a parallelepiped, with 6 faces, which can be rotated so that we see a particular face in front of us.
Each such rotation corresponds to a particular permutation of
The elementary operation that change the permutation of the index is an "axis swap" (e.g. ijk -> kji where i and k have been swapped).
A ciclical shift of the axes corresponds to two successive swapping on two orthogonal faces
$ijk \rightarrow jki = ijk \rightarrow kji \rightarrow jki$
# swapaxis can do wathever volume rotation. It interchanges two axes of an array.
# here let's swap axis 0 and 2 (first and last)
# 3,2,4 -> 4,2,3
b=a.swapaxes(0,2)
print(b)
# The array data has changed
print("data of array a:", ''.join(a.flatten()))
print("data of array b:", ''.join(b.flatten()))
HTML(asvolume(b))[[['♥' '♥' '♥']
['♥' '♥' '♥']]
[['♣' '♣' '♣']
['♣' '♣' '♣']]
[['♦' '♦' '♦']
['♦' '♦' '♦']]
[['♠' '♠' '♠']
['♠' '♠' '♠']]]
data of array a: ♥♣♦♠♥♣♦♠♥♣♦♠♥♣♦♠♥♣♦♠♥♣♦♠
data of array b: ♥♥♥♥♥♥♣♣♣♣♣♣♦♦♦♦♦♦♠♠♠♠♠♠
| ♥ | ♥ | ♥ | |||
| ♥ | ♣ | ♣ | ♣ | ||
| ♣ | ♦ | ♦ | ♦ | ||
| ♦ | ♠ | ♠ | ♠ | ||
| ♠ | ♠ | ♠ |
# rot90(a, axes=(0,1))
# Rotate an array by 90 degrees in the plane specified by axes.
# Rotation direction is from the first towards the second axis.
# it is equivalent to swapaxis(...) but with a more understandable logic
b=np.rot90(a,axes=(2,0))
print(b)
HTML(asvolume(b))[[['♥' '♥' '♥']
['♥' '♥' '♥']]
[['♣' '♣' '♣']
['♣' '♣' '♣']]
[['♦' '♦' '♦']
['♦' '♦' '♦']]
[['♠' '♠' '♠']
['♠' '♠' '♠']]]
| ♥ | ♥ | ♥ | |||
| ♥ | ♣ | ♣ | ♣ | ||
| ♣ | ♦ | ♦ | ♦ | ||
| ♦ | ♠ | ♠ | ♠ | ||
| ♠ | ♠ | ♠ |
# we can rotate in the opporsite direction and look the volume from behind
# (look at the order of the symbols)
b=np.rot90(a,axes=(0,2))
print(b)
HTML(asvolume(b))[[['♠' '♠' '♠']
['♠' '♠' '♠']]
[['♦' '♦' '♦']
['♦' '♦' '♦']]
[['♣' '♣' '♣']
['♣' '♣' '♣']]
[['♥' '♥' '♥']
['♥' '♥' '♥']]]
| ♠ | ♠ | ♠ | |||
| ♠ | ♦ | ♦ | ♦ | ||
| ♦ | ♣ | ♣ | ♣ | ||
| ♣ | ♥ | ♥ | ♥ | ||
| ♥ | ♥ | ♥ |
# then we might want to rotate it on its front face
c=np.rot90(b,axes=(1,2))
print(c) # rotate on front face
HTML(asvolume(c))[[['♠' '♠']
['♠' '♠']
['♠' '♠']]
[['♦' '♦']
['♦' '♦']
['♦' '♦']]
[['♣' '♣']
['♣' '♣']
['♣' '♣']]
[['♥' '♥']
['♥' '♥']
['♥' '♥']]]
| ♠ | ♠ | |||
| ♠ | ♦ | ♦ | ||
| ♠ | ♦ | ♣ | ♣ | |
| ♦ | ♣ | ♥ | ♥ | |
| ♣ | ♥ | ♥ | ||
| ♥ | ♥ |
# Finally, the most powerful einsum
# which can do rotations and much more.
# np.einsum('abc->cba',a)
b = np.einsum('abc->cab',a)
print(b)
HTML(asvolume(b))[[['♥' '♥']
['♥' '♥']
['♥' '♥']]
[['♣' '♣']
['♣' '♣']
['♣' '♣']]
[['♦' '♦']
['♦' '♦']
['♦' '♦']]
[['♠' '♠']
['♠' '♠']
['♠' '♠']]]
| ♥ | ♥ | |||
| ♥ | ♣ | ♣ | ||
| ♥ | ♣ | ♦ | ♦ | |
| ♣ | ♦ | ♠ | ♠ | |
| ♦ | ♠ | ♠ | ||
| ♠ | ♠ |
# transform array 'u' (2D) into array 'v' (3D)
u=np.array([['♥']*15, ['♣']*15, ['♦']*15, ['♠']*15])
v=np.array([[['♥']*5, ['♣']*5, ['♦']*5, ['♠']*5]]*3 )
print(u)
print()
print(v)
HTML(asvolume(v))[['♥' '♥' '♥' '♥' '♥' '♥' '♥' '♥' '♥' '♥' '♥' '♥' '♥' '♥' '♥']
['♣' '♣' '♣' '♣' '♣' '♣' '♣' '♣' '♣' '♣' '♣' '♣' '♣' '♣' '♣']
['♦' '♦' '♦' '♦' '♦' '♦' '♦' '♦' '♦' '♦' '♦' '♦' '♦' '♦' '♦']
['♠' '♠' '♠' '♠' '♠' '♠' '♠' '♠' '♠' '♠' '♠' '♠' '♠' '♠' '♠']]
[[['♥' '♥' '♥' '♥' '♥']
['♣' '♣' '♣' '♣' '♣']
['♦' '♦' '♦' '♦' '♦']
['♠' '♠' '♠' '♠' '♠']]
[['♥' '♥' '♥' '♥' '♥']
['♣' '♣' '♣' '♣' '♣']
['♦' '♦' '♦' '♦' '♦']
['♠' '♠' '♠' '♠' '♠']]
[['♥' '♥' '♥' '♥' '♥']
['♣' '♣' '♣' '♣' '♣']
['♦' '♦' '♦' '♦' '♦']
['♠' '♠' '♠' '♠' '♠']]]
| ♥ | ♥ | ♥ | ♥ | ♥ | ||
| ♣ | ♥ | ♥ | ♥ | ♥ | ♥ | |
| ♦ | ♣ | ♥ | ♥ | ♥ | ♥ | ♥ |
| ♠ | ♦ | ♣ | ♣ | ♣ | ♣ | ♣ |
| ♠ | ♦ | ♦ | ♦ | ♦ | ♦ | |
| ♠ | ♠ | ♠ | ♠ | ♠ |
# let's first reshape 'a' into a 3D shape
# what we want is 3 pages of the same symbol
w=u.reshape(4,3,5)
print(w)
HTML(asvolume(w))[[['♥' '♥' '♥' '♥' '♥']
['♥' '♥' '♥' '♥' '♥']
['♥' '♥' '♥' '♥' '♥']]
[['♣' '♣' '♣' '♣' '♣']
['♣' '♣' '♣' '♣' '♣']
['♣' '♣' '♣' '♣' '♣']]
[['♦' '♦' '♦' '♦' '♦']
['♦' '♦' '♦' '♦' '♦']
['♦' '♦' '♦' '♦' '♦']]
[['♠' '♠' '♠' '♠' '♠']
['♠' '♠' '♠' '♠' '♠']
['♠' '♠' '♠' '♠' '♠']]]
| ♥ | ♥ | ♥ | ♥ | ♥ | |||
| ♥ | ♣ | ♣ | ♣ | ♣ | ♣ | ||
| ♥ | ♣ | ♦ | ♦ | ♦ | ♦ | ♦ | |
| ♣ | ♦ | ♠ | ♠ | ♠ | ♠ | ♠ | |
| ♦ | ♠ | ♠ | ♠ | ♠ | ♠ | ||
| ♠ | ♠ | ♠ | ♠ | ♠ |
# now what we have to do is just rotate the volume
# we send the first axes (pointing at you from the first to the last page) into the second axis (page rows, top to bottom)
t=np.rot90(w,axes=(0,1))
print(t)
HTML(asvolume(t))[[['♥' '♥' '♥' '♥' '♥']
['♣' '♣' '♣' '♣' '♣']
['♦' '♦' '♦' '♦' '♦']
['♠' '♠' '♠' '♠' '♠']]
[['♥' '♥' '♥' '♥' '♥']
['♣' '♣' '♣' '♣' '♣']
['♦' '♦' '♦' '♦' '♦']
['♠' '♠' '♠' '♠' '♠']]
[['♥' '♥' '♥' '♥' '♥']
['♣' '♣' '♣' '♣' '♣']
['♦' '♦' '♦' '♦' '♦']
['♠' '♠' '♠' '♠' '♠']]]
| ♥ | ♥ | ♥ | ♥ | ♥ | ||
| ♣ | ♥ | ♥ | ♥ | ♥ | ♥ | |
| ♦ | ♣ | ♥ | ♥ | ♥ | ♥ | ♥ |
| ♠ | ♦ | ♣ | ♣ | ♣ | ♣ | ♣ |
| ♠ | ♦ | ♦ | ♦ | ♦ | ♦ | |
| ♠ | ♠ | ♠ | ♠ | ♠ |
u=np.array([[22]*15, [27]*15, [32]*15, [37]*15])
v=u.reshape(4,3,5)
HTML(asvolume(v))| 22 | 22 | 22 | 22 | 22 | |||
| 22 | 27 | 27 | 27 | 27 | 27 | ||
| 22 | 27 | 32 | 32 | 32 | 32 | 32 | |
| 27 | 32 | 37 | 37 | 37 | 37 | 37 | |
| 32 | 37 | 37 | 37 | 37 | 37 | ||
| 37 | 37 | 37 | 37 | 37 |
HTML(asvolume(np.rot90(v,axes=(0,1)), color=True))| 22 | 22 | 22 | 22 | 22 | ||
| 27 | 22 | 22 | 22 | 22 | 22 | |
| 32 | 27 | 22 | 22 | 22 | 22 | 22 |
| 37 | 32 | 27 | 27 | 27 | 27 | 27 |
| 37 | 32 | 32 | 32 | 32 | 32 | |
| 37 | 37 | 37 | 37 | 37 |