Numpy
Numpy
import numpy as np
import pandas as pd
#Example showing NumPy arrays take less storage space than lists,
tuples and Pandas DataFrame for the same elements
tuple_ex = tuple(range(1000))
list_ex = list(range(1000))
numpy_ex = np.array([range(1000)])
pandas_df = pd.DataFrame(range(1000))
print("Space taken by tuple =",tuple_ex.__sizeof__()," bytes")
print("Space taken by list =",list_ex.__sizeof__()," bytes")
print("Space taken by Pandas DataFrame =",pandas_df.__sizeof__(),"
bytes")
print("Space taken by NumPy array =",numpy_ex.__sizeof__()," bytes")
start_time = tm.time()
tuple_ex = tuple(range(1000000)) #Tuple containinig whole numbers upto
1 million
a=(tuple_ex*2)
print("Time take to multiply numbers in a tuple = ", tm.time()-
start_time)
start_time = tm.time()
df_ex = pd.DataFrame(range(1000000)) #Pandas DataFrame containinig
whole numbers upto 1 million
a=(df_ex*2)
print("Time take to multiply numbers in a Pandas DataFrame = ",
tm.time()-start_time)
start_time = tm.time()
numpy_ex = np.arange(1000000) #NumPy array containinig whole numbers
upto 1 million
a=(numpy_ex*2)
print("Time take to multiply numbers in a NumPy array = ", tm.time()-
start_time)
# Scalar
x = np.array(6)
print ("x: ", x)
print ("x ndim: ", x.ndim) # number of dimensions
print ("x shape:", x.shape) # dimensions
print ("x size: ", x.size) # size of elements
print ("x dtype: ", x.dtype) # data type
x: 6
x ndim: 0
x shape: ()
x size: 1
x dtype: int32
# Vector
x = np.array([1.3 , 2.2 , 1.7])
print ("x: ", x)
print ("x ndim: ", x.ndim)
print ("x shape:", x.shape)
print ("x size: ", x.size)
print ("x dtype: ", x.dtype) # notice the float datatype
# Matrix
x = np.array([[1,2], [3,4]])
print ("x:\n", x)
print ("x ndim: ", x.ndim)
print ("x shape:", x.shape)
print ("x size: ", x.size)
print ("x dtype: ", x.dtype)
x:
[[1 2]
[3 4]]
x ndim: 2
x shape: (2, 2)
x size: 4
x dtype: int32
# 3-D Tensor
x = np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
print ("x:\n", x)
print ("x ndim: ", x.ndim)
print ("x shape:", x.shape)
print ("x size: ", x.size)
print ("x dtype: ", x.dtype)
x:
[[[1 2]
[3 4]]
[[5 6]
[7 8]]]
x ndim: 3
x shape: (2, 2, 2)
x size: 8
x dtype: int32
NumPy also comes with several functions that allow us to create tensors quickly.
# Functions
print ("np.zeros((2,2)):\n", np.zeros((2,2)))
print ("np.ones((2,2)):\n", np.ones((2,2)))
print ("np.eye((2)):\n", np.eye((2))) # identity matrix
print ("np.random.random((2,2)):\n", np.random.random((2,2)))
np.zeros((2,2)):
[[0. 0.]
[0. 0.]]
np.ones((2,2)):
[[1. 1.]
[1. 1.]]
np.eye((2)):
[[1. 0.]
[0. 1.]]
np.random.random((2,2)):
[[0.37896304 0.33762979]
[0.31476591 0.0377045 ]]
Indexing
We can extract specific values from our tensors using indexing.
Keep in mind that when indexing the row and column, indices start at 0. And like indexing with
lists, we can use negative indices as well (where -1 is the last item).
# Indexing
x = np.array([1, 2, 3])
print ("x: ", x)
print ("x[0]: ", x[0])
x[0] = 0
print ("x: ", x)
x: [1 2 3]
x[0]: 1
x: [0 2 3]
# Slicing
x = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
print (x)
print ("x column 1: ", x[:, 1])
print ("x row 0: ", x[0, :])
print ("x rows 0,1 & cols 1,2: \n", x[0:2, 1:3])
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
x column 1: [ 2 6 10]
x row 0: [1 2 3 4]
x rows 0,1 & cols 1,2:
[[2 3]
[6 7]]
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
rows_to_get: [0 1 2]
cols_to_get: [0 2 1]
indexed values: [ 1 7 10]
Arithmetic
# Basic math
x = np.array([[1,2], [3,4]], dtype=np.float64)
y = np.array([[1,2], [3,4]], dtype=np.float64)
print ("x + y:\n", np.add(x, y)) # or x + y
print ("x - y:\n", np.subtract(x, y)) # or x - y
print ("x * y:\n", np.multiply(x, y)) # or x * y
x + y:
[[2. 4.]
[6. 8.]]
x - y:
[[0. 0.]
[0. 0.]]
x * y:
[[ 1. 4.]
[ 9. 16.]]
Dot product
One of the most common NumPy operations we’ll use in machine learning is matrix
multiplication using the dot product. Suppose we wanted to take the dot product of two
matrices with shapes [2 X 3] and [3 X 2]. We take the rows of our first matrix (2) and the columns
of our second matrix (2) to determine the dot product, giving us an output of [2 X 2]. The only
requirement is that the inside dimensions match, in this case the first matrix has 3 columns and
the second matrix has 3 rows.
# Dot product
a = np.array([[1,2,3], [4,5,6]], dtype=np.float64) # we can specify
dtype
b = np.array([[7,8], [9,10], [11, 12]], dtype=np.float64)
c = a.dot(b)
print (f"{a.shape} · {b.shape} = {c.shape}")
print (c)
Axis operations
We can also do operations across a specific axis.
# Sum across a dimension
x = np.array([[1,2],[3,4]])
print (x)
print ("sum all: ", np.sum(x)) # adds all elements
print ("sum axis=0: ", np.sum(x, axis=0)) # sum across rows
print ("sum axis=1: ", np.sum(x, axis=1)) # sum across columns
[[1 2]
[3 4]]
sum all: 10
sum axis=0: [4 6]
sum axis=1: [3 7]
# Min/max
x = np.array([[1,2,7], [4,5,6]])
print ("min: ", x.min())
print ("max: ", x.max())
print ("min axis=0: ", x.min(axis=0))
print ("min axis=1: ", x.min(axis=1))
min: 1
max: 7
min axis=0: [1 2 6]
min axis=1: [1 4]
# Broadcasting
x = np.array([1,2]) # vector
y = np.array(3) # scalar
z = x + y
print ("z:\n", z)
z:
[4 5]
Transpose
We often need to change the dimensions of our tensors for operations like the dot product. If we
need to switch two dimensions, we can transpose the tensor.
# Transposing
x = np.array([[1,2,3], [4,5,6]])
print ("x:\n", x)
print ("x.shape: ", x.shape)
y = np.transpose(x, (1,0)) # flip dimensions at index 0 and 1
print ("y:\n", y)
print ("y.shape: ", y.shape)
x:
[[1 2 3]
[4 5 6]]
x.shape: (2, 3)
y:
[[1 4]
[2 5]
[3 6]]
y.shape: (3, 2)
Reshape
Sometimes, we'll need to alter the dimensions of the matrix. Reshaping allows us to transform a
tensor into different permissible shapes. Below, our reshaped tensor has the same number of
values as the original tensor. (1X6 = 2X3). We can also use -1 on a dimension and NumPy will
infer the dimension based on our input tensor.
# Reshaping
x = np.array([[1,2,3,4,5,6]])
print (x)
print ("x.shape: ", x.shape)
y = np.reshape(x, (2, 3))
print ("y: \n", y)
print ("y.shape: ", y.shape)
z = np.reshape(x, (2, -1))
print ("z: \n", z)
print ("z.shape: ", z.shape)
[[1 2 3 4 5 6]]
x.shape: (1, 6)
y:
[[1 2 3]
[4 5 6]]
y.shape: (2, 3)
z:
[[1 2 3]
[4 5 6]]
z.shape: (2, 3)
The way reshape works is by looking at each dimension of the new tensor and separating our
original tensor into that many units. So here the dimension at index 0 of the new tensor is 2 so
we divide our original tensor into 2 units, and each of those has 3 values.