Chapter 11, Basic
Reshape and transpose
The same data, in another form. Almost every library function expects a specific form, and reshaping is how you deliver it.
Code for this chapter: básico/cap11_reshape.py
Reshape without changing the data
The reshape rearranges the same elements into another form. The total needs to remain the same: it never creates or removes elements. And the -1 in one of the dimensions means "calculate yourself":
import numpy as np
arr = np.arange(12)
print(arr.reshape(3, 4))
print(arr.reshape(2, -1).shape, arr.reshape(-1, 3).shape)
try:
arr.reshape(5, 3)
except ValueError as erro:
print(erro)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
(2, 6) (4, 3)
cannot reshape array of size 12 into shape (5,3)
Five times three equals 15, and the array has 12 elements, so NumPy refuses. The reshape(2, -1) figured out on its own that the second dimension is 6.
Transpose, flatten, and insert axes
grade = np.arange(6).reshape(2, 3)
print(grade.T.shape, grade.transpose().shape)
print(grade.flatten(), grade.ravel())
print(grade[:, np.newaxis, :].shape, grade[..., None].shape)
print(np.squeeze(np.zeros((1, 3, 1))).shape)
(3, 2) (3, 2)
[0 1 2 3 4 5] [0 1 2 3 4 5]
(2, 1, 3) (2, 3, 1)
(3,)
| Function | What it does |
|---|---|
a.T or a.transpose() | Swaps rows and columns. In more dimensions, it reverses the order of the axes |
a.swapaxes(i, j) | Swaps two specific axes |
a.flatten() | Flattens to 1D and always copies |
a.ravel() | Flattens to 1D and returns a view when possible |
np.newaxis or None | Inserts an axis of size 1 |
np.squeeze(a) | Removes axes of size 1 |
The difference between flatten and ravel is the same as in the previous chapter: one copies, the other shares memory, and changing the result of ravel changes the original:
r = grade.ravel()
r[0] = 99
print(grade[0, 0])
f = grade.flatten()
f[0] = -1
print(grade[0, 0])
99
99
The extra axis trick
Inserting an axis of size 1 is the way to prepare an array for the broadcasting of the next chapter. An array of shape (3,) becomes a column (3, 1) with reshape(-1, 1) or with [:, None]:
vetor = np.array([1, 2, 3])
print(vetor.reshape(-1, 1))
[[1]
[2]
[3]]
A real case: batch of images
A batch of 10 images of 28 by 28 pixels has shape (10, 28, 28). Many models expect each image as a vector of 784 numbers, that is, shape (10, 784). The -1 calculates the 784 by itself:
lote = np.zeros((10, 28, 28))
print(lote.reshape(10, -1).shape)
(10, 784)
reshape returns a view when it can
The
reshapereturns a view if memory allows and only copies when there is no other way. Therefore, after areshape, keep in mind that changing the result can change the original. If in doubt, check withnp.shares_memory.
Exercise 1
Column and spreadsheet
Write para_coluna(v) (a 1D vector becomes a column) and lote_para_planilha(imagens) (from (n, h, w) to (n, h*w)).