Numpy is a numerical computing Python library which provides n-dimensional arrays.
If we were to implement arrays on python we would make use of lists which are slow to process. Meanwhile Numpy uses C++ on its background to access and manipulate arrays very efficiently on a continuous space.
Usually numpy is imported under the alias np
import numpy as np
print( 'Version:', np.__version__ )
Numpy arrays are of type ndarray. To create an array we have to pass a list/tupples/array-like object-nested sequences to the np.array function.
a = np.array( [1, 2, 3] )# Create array
print( 'type', type(a) )
print( 'shape', a.shape )
print( 'elements:', a[0], a[1], a[2] )
a[0] = 5# Change an element of the array
print( a )
0-D arrays are scalar values
a = np.array( 27 )
a.shape
a
type( a.item() )
a
List that consists of 0-D arrays
a = np.array( [40,50,60] )
a.shape
a
print( a )
List that consists of 1-D arrays
b = np.array( [
[4,5,6],
[9,8,7]
])
print( 'Shape', b.shape )
b
print( b )
List that consists of 2-D arrays. In general N-D arrays, list that consists of (N-1)-D arrays.
c = np.array( [
[
[4,5,6],
[9,8,7]
],
[[44,55,66],
[99,88,77]],
])
print( c )
print( 'Shape', c.shape )
Question: Find the position of number 8 in the 3d array
#c[ ][ ][ ]
Numpy also provides many functions to create arrays:
All of zeros
a = np.zeros( (5,5,10) )#,43,5,4,2,4,2,4,3) )
a.shape
All of ones
b = np.ones( (10,10) )
b
Identity matrix
c = np.eye(7)
c
d = np.eye(7,10)
d
Matrix with all its cells sets to a value
e = np.full( (6,8), 4 )
e
To iterate through an array use the shape attribute to get its dimensions.
a = np.array( [ [43,42], [10,5], [27,8], [85,52] ] )
print( 'Shape', a.shape )
print('Values')
for i in range( a.shape[0] ):
for j in range( a.shape[1] ):
print( a[ i,j ], end=' ' )
print()
a
To refer to some cell of matrix a we use a[idx1][idx2]...[idxn] per dimension or a[idx1,idx2,...,idxn]
a[1][1]
a[1,1]
Function np.arange(n) return all numbers from [0,n)
a = np.arange(100).reshape( (10,10) )
a
To refer to subarrays use the notation below
array[ firstRow:lastRow, firstCol:lastCol, ... ]
where firstRow:lastRow and firstCol:lastCol are some list
b = a[ 1:4, 1:5 ]
b
c = a[ 1 ]
c
a[1].shape
c.shape
d = a[1]
d
d.shape
a[ 1:4, 1:5 ] = 50
a
d = np.arange( 10 ).reshape( (1,1,1,1,1,1,10) )
print( d )
print( d.shape )
e = d.reshape( (2,5) )
e
e.shape
d = np.arange(10).reshape( (5,2) )
print( d.shape )
print( d.T.shape )
np.transpose(d).T
Exercise: Make use of the function np.tranpose or T attribute of the ndarray object.
$tr(A) = A^{T}$
Write the corresponding code below each comment
a = np.array( [ [2,4,5,2,1], [45,2,45,1,76] ] )
print(a)
a.shape
print( np.transpose(a), np.transpose(a).shape )
print( a.T, a.T.shape )
#Create an array of size 2x5
#print array and its shape
#use np.transpose( array ) or array.T attribute to calculate the tranpose of array
#print tranposed array and its shape
Notice that 1-D arrays have a shape of (N,) instead of (N,1).
a = np.array( [ [1,2,3] ] )
a
a.T
a.T.shape
a.T.T.shape
Exercise: What do you notice at the result of the tranpose? Is it correct? Justify your answer.
a = np.array( [ [1,2,3,4,5], [1,2,3,4,5] ] )
b = a
a
b
a[0,1] = 7
a
b
b = a.copy()
a[0,1] = 10
a
b
For nested sequences/lists use deepcopy from copy library
from copy import deepcopy
"""
For example
E.x.
b = [
np.array(...),
np.array(...),
[
[
[ np.array(...)] ] ], [] ]
a = copy(b)
"""
a = np.array( [ [1,2,3,4,5], [1,2,3,4,5] ] )
a
Horizontal
np.concatenate( [ a, a ], axis=0 )
np.concatenate( [a,a], axis=1 )
Exercise:
$ B = \begin{bmatrix} 1 & 4 & 7 \\ 2 & 5 & 8 \\ 3 & 6 & 9 \end{bmatrix} $
Create matrix $[ I_{3} B ]$
a = np.arange( 20 ) + 1
b = np.array_split( a, 5 )
b
np.random.random return numbers $\in [0,1)$
b = np.random.random( (10,3) )
b
a = np.random.permutation( 10 )[0:3]
b[ a ]
Arrays are defined to be within a data type ex. np.float32, np.float64, np.int8, np.int16, np.32, np.in64
a = np.arange(10).reshape( (2,5) )
b = np.array( [ [2.5,3.2], [6.8,8.9] ] )
a.dtype
b.dtype
c = np.array( [ [2.5,3.2], [6.8,8.9] ],
dtype=np.float32 )
c
d = np.array( [ [3,5], [8,13] ], dtype=np.int8 )
d
To convert to another datatype use astype function
f = d.astype( np.float64 )
f
f.dtype
Addition
import numpy as np
a = np.arange(25).reshape( (5,5) )
a
As you would expect the additon is executed elemtwise
a + a
Likewise for subtraction
a - a
Multiplication
Elementwise multiplication
a * a
np.multiply(a,a)
For matrix multiplication use np.dot
a
np.dot(a,a)
Matrix x Vector
$$A \times x = b$$$$ (N \times m) \times (M\times1) = N \times 1$$x = np.array( [0,5,10,15,20] ).reshape( (-1,1) )
x
np.dot(a, x )
Vector x Vector
x
Dot/Inner/Scalar product
$x^{T} \times x$
np.dot(x.T,x)
Elementwise
np.multiply(x,x)
x * x
When there is a mismatch on the array shapes, operation are still possible under certain conditions. For example in the example below the array [4,8,12] is replicated to match the dimension of the matrix its being added to.
a = np.zeros( (5,5) )
a
b = np.array( [1,2,3,4,5] ).reshape( (-1,1) )
b
broadcast per row
a + b.T
broadcast per column
a + b.T
Exercise: For each cell calculate its percentage per row
a = np.array( [ [1,1,2], [24,42,12], [10,20,30], [80,40,20] ] ).T
a
#hint use np.sum( ..., axis=0 ) to get the sum per col
Instead of broadcasting we could repeat the pattern using the np.tile( array, shape )
a = np.array( [1,2,3] ).reshape( (-1,1) )
a
np.tile( a, (1,1) )
np.tile( a, (1,10) )
np.tile( a, (3,1) )
np.tile( a, (2,3) )
Determinant
a = np.array( [ [10,20], [30,40] ] )
b = np.linalg.det(a)
print(a)
b
Inversion
a = np.array( [ [10,20], [30,40] ] )
b = np.linalg.inv( a )
a
b
b.shape
np.dot( a, b )
Argmax/Argmax
a = np.eye(5)
a
a[ np.arange(5) ] = a[ np.random.permutation(5) ]
a
This kind of encoding is known as one-hot vector in machine learning. Instead of zeros and ones it could have real values to represent probabilities. Each row represent its percentage to be classified to a specific category(e.x. 5 columns = 5 classes/categories ). So argmax per row would choose the category that has the maximum probability/value.
np.argmax( a, axis=1 )
Given a condition returns the indices per dimension
a = np.arange( 15 ).reshape(3,5)
b = np.where( a <= 5 )
a
print( 'X', b[0] )
print( 'Y', b[1] )