import numpy as np
Numpy ndarrays
1 Numpy
= np.array([1.0, 2.0]) x
We can construct ndarray
from Python lists of numbers.
print(f"v={x}")
print(f"type(x)={type(x)}")
v=[1. 2.]
type(x)=<class 'numpy.ndarray'>
#
# sometimes we need to be more explicit if we want to control
# the data type used by the ndarray.
#
= np.array([1, 2, 3, 4, 5], dtype=np.float32)
x x
array([1., 2., 3., 4., 5.], dtype=float32)
#
# we can inspect a ndarray in many ways
#
print(f"x.dtype = {x.dtype}")
print(f"x.size = {x.size}")
print(f"x.shape = {x.shape}")
x.dtype = float32
x.size = 5
x.shape = (5,)
x.dtype
is the datatype of each element in the ndarrayx.size
is the total number of elements of the ndarrayx.shape
is a tuple that describes the layout of the ndarray.
2 Multiple axes
Each axis is a dimension in the array.
- Scalar has zero axes.
- Vector has one axis, so every element is identified by one index.
- Matrix has two axes, so each element is identified by two indices.
- Tensors have multiple axes.
Each axis has a size.
The shape of a
ndarray
is the tuple of the sizes of all its axes.
2.1 Matrices
= np.array([
x 1, 2, 3],
[4, 5, 6]
[=np.float32)
], dtype x
array([[1., 2., 3.],
[4., 5., 6.]], dtype=float32)
x.shape
(2, 3)
We can see that x
has two axes. Axis 0 has two indices, and axis 1 has three indices.
0][2] x[
3.0
2.2 Higher order tensors
= np.array([
x 1, 2, 3],
[[4, 5, 6]],
[-1, -2, -3],
[[-4, -5, -6]]
[
]) x
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[-1, -2, -3],
[-4, -5, -6]]])
x.shape
(2, 2, 3)
3 Reshaping
A ndarray can change its shape as long as the size doesn’t change.
Thus, we can perform the following reshaping. \[ \left[ \begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \end{array} \right] \underset{\mathrm{reshape}(3,2)}{\longrightarrow} \left[ \begin{array}{ccc} 1 & 2 \\ 3 & 4 \\ 5 & 6 \end{array} \right] \]
Note, both the source tensor and target tensor share the same iteration over the elements:
for i in axis_0: for j in axis_1: yield tensor[i,j]
= np.array([
x 1, 2, 3],
[4, 5, 6]
[
]) x
array([[1, 2, 3],
[4, 5, 6]])
3,2)) x.reshape((
array([[1, 2],
[3, 4],
[5, 6]])
But we can go further. We can reshape between tensors of different axes.
\[ \left[ \begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \end{array} \right] \underset{\mathrm{reshape(2,3,1)}}{\longrightarrow} \left[ \begin{array}{ccc} [1] & [2] & [3] \\ [4] & [5] & [6] \end{array} \right] \]
2,3,1)) x.reshape((
array([[[1],
[2],
[3]],
[[4],
[5],
[6]]])
When we perform reshaping, at most one axis size can be left as -1
, which will be automatically computed so that the resulting tensor size remains the same.
#
# Note that the column-count can be left as -1 because
# it can be inferred by `reshape(...)`.
#
3, -1)) x.reshape((
array([[1, 2],
[3, 4],
[5, 6]])
-1) x.reshape(
array([1, 2, 3, 4, 5, 6])
A common trick to flatten a tensor is to reshape to a single axis with size -1
.
4 Tensor construction
There are many functions that help us construct tensors.
- tensor with constant values
- tensor with sequential values
- tensor with random values
4.1 Constant values
3, 2)) np.zeros((
array([[0., 0.],
[0., 0.],
[0., 0.]])
3,2)) np.ones((
array([[1., 1.],
[1., 1.],
[1., 1.]])
4.2 Sequential values
0, 12) np.arange(
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
-1, 1, 12) np.linspace(
array([-1. , -0.81818182, -0.63636364, -0.45454545, -0.27272727,
-0.09090909, 0.09090909, 0.27272727, 0.45454545, 0.63636364,
0.81818182, 1. ])
#
# Using reshape, we can get sequential values into a tensor of any shape.
#
12).reshape(2,2,3) np.arange(
array([[[ 0, 1, 2],
[ 3, 4, 5]],
[[ 6, 7, 8],
[ 9, 10, 11]]])
4.3 Random values
0, 100, size=(2,3)) np.random.randint(
array([[90, 48, 68],
[80, 31, 75]])
#
# - each element is from the uniform distribution from [0, 1]
# - shape is as given
#
2, 3) np.random.rand(
array([[0.90239838, 0.47738473, 0.58395778],
[0.73485272, 0.54816791, 0.23889894]])
#
# - each element is from normal distribution with $\mu=0$, $\sigma=1$.
# - shape is as given
#
2, 3) np.random.randn(
array([[-1.2246003 , -0.44247578, -0.94061533],
[ 0.60463683, -1.00250709, -0.34248086]])