Numpy¶

In [1]:
# import numpy package 
import numpy as np 
In [2]:
theta_list = [-1.0, 1.0, 4.0]
In [3]:
# Create a 1-D array (a vector) by passing in a Python list
theta_array = np.array(theta_list)
In [4]:
# To examine the dimensions of an array, use .shape 
print('Shape of theta_array:', theta_array.shape)
Shape of theta_array: (3,)
In [5]:
# Indexing is the same for Python lists and numpy arrays 
print('Second element of theta_list = ', theta_list[1])
print('Second element of theta_array = ', theta_array[1])
Second element of theta_list =  1.0
Second element of theta_array =  1.0
In [6]:
# Another 1-D array 
x_array = np.array([1, 1, 1])
In [7]:
# Can also create 2-D arrays (and higher dimensions)
x2D_array = np.array([[1,2,3], 
                       [4, 5, 6]])
print('Shape of x2D_array =', x2D_array.shape)
print('\t num. rows =', x2D_array.shape[0])
print('\t num. columns =', x2D_array.shape[1])
Shape of x2D_array = (2, 3)
	 num. rows = 2
	 num. columns = 3
In [8]:
# Dot product between two arrays 
dot_product = np.dot(x_array, theta_array)
print('Dot product (np.dot) =', dot_product)
Dot product (np.dot) = 4.0
In [9]:
# np.dot much more efficient than for-loops by hand 
dot_product = 0 
for i in range(x_array.shape[0]): 
    dot_product += theta_array[i]*x_array[i]
print('Dot product (by hand) =', dot_product)
Dot product (by hand) = 4.0
In [11]:
# Note: by definition, dot products must have the same shape 
# If not, numpy gives an error 
x2_array = np.array([1, 2, 3, 4])
np.dot(theta_array, x2_array)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[11], line 4
      1 # Note: by definition, dot products must have the same shape 
      2 # If not, numpy gives an error 
      3 x2_array = np.array([1, 2, 3, 4])
----> 4 np.dot(theta_array, x2_array)

File <__array_function__ internals>:180, in dot(*args, **kwargs)

ValueError: shapes (3,) and (4,) not aligned: 3 (dim 0) != 4 (dim 0)
In [12]:
# Array broadcasting is one of the most powerful and efficient aspects
# of numpy because it allows you to write intuitive code with arrays 
In [13]:
x_array
Out[13]:
array([1, 1, 1])
In [14]:
# Array broadcasting applies the float(scalar) element-wise 
3*x_array
Out[14]:
array([3, 3, 3])
In [15]:
4+x_array
Out[15]:
array([5, 5, 5])
In [16]:
# Matrices 
X = np.array([[1, 1, 1],
                [2, 0, 1],
                [1, 2, 3]])
X.shape
Out[16]:
(3, 3)
In [17]:
X.dot(theta_array)
Out[17]:
array([ 4.,  2., 13.])
In [23]:
#it's applying via each row of X 
In [18]:
X[0]
Out[18]:
array([1, 1, 1])
In [19]:
X[0].dot(theta_array)
Out[19]:
4.0
In [20]:
X[1].dot(theta_array)
Out[20]:
2.0
In [21]:
X[2].dot(theta_array)
Out[21]:
13.0
In [22]:
# Careful! Matrix multiplication is not transitive 
theta_array.dot(X)
Out[22]:
array([ 5.,  7., 12.])