import numpy as np

Numpy#

NumPy (Numerical Python) is a powerful library in Python for numerical and mathematical operations. We will give a brief overview of NumPy here, feel free to look here for more details.

Arrays#

The heart of NumPy lies in the arrays. NumPy arrays are similar to Python lists, but much faster if you have a large number of elements. They have two key differences compared to Python lists:

  • NumPy arrays have a fixed size, you cannot directly add an element like you could in a list. Instead, you have to use a function such as np.append, which technically create another array.

  • NumPy arrays cannot store objects of different data types and will instead convert everything to the same data type.

These are demonstrated below.

sample_list = [1,2,3] # creating a sample list
sample_array = np.array([1,2,3]) # creating a sample array
sample_list, sample_array
([1, 2, 3], array([1, 2, 3]))
sample_list_appended = sample_list + [4] # you can append to a list by creating another list
sample_array_appended = np.append(sample_array, 4) # appending to arrays requires using a function
sample_list_appended, sample_array_appended
([1, 2, 3, 4], array([1, 2, 3, 4]))
sample_list_str = sample_list_appended + ['woah'] # lists can store different types of data
sample_array_str = np.append(sample_array_appended, 'woah') # arrays cannot, they convert everything to a string here
sample_list_str, sample_array_str
([1, 2, 3, 4, 'woah'], array(['1', '2', '3', '4', 'woah'], dtype='<U21'))

Array Operations#

You can easily do math and add/subtract arrays with scalars and with other arrays that have the same dimensions, as shown below.

np.array([1, 2, 3, 4]) + 1 # adding 1
array([2, 3, 4, 5])
np.array([1, 2, 3, 4]) * 8 # multiplying by 8
array([ 8, 16, 24, 32])
np.array([1, 2, 3, 4]) + np.array([5, 6, 7, 8]) # adding two arrays with the same dimensions
array([ 6,  8, 10, 12])
np.array([1, 2, 3, 4]) ** 2 # squaring
array([ 1,  4,  9, 16])

NumPy also has several built-in functions you can use to work on arrays of numbers; examples shown below.

sample_array_appended
array([1, 2, 3, 4])
np.sum(sample_array_appended)
10
np.mean(sample_array_appended)
2.5
np.min(sample_array_appended), np.max(sample_array_appended)
(1, 4)
np.std(sample_array_appended)
1.118033988749895

Matrices#

Moreover, you can also define matrices as 2-dimensional numpy arrays, as shown below:

first_matrix = np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])
first_matrix
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

You can use the .shape attribute for seeing the dimensions (number of rows/columns) of the numpy array.

first_matrix.shape
(3, 3)

The range function#

The np.arange function is very helpful for defining new arrays of numbers (read more here). Examples are shown below.

np.arange(10) #Starts at 0, ends at 9 (non-inclusive on the ending side), gives you all whole numbers
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.arange(0, 10, 2) # Starts at 0, ends at 9, gives you numbers in increments of 2
array([0, 2, 4, 6, 8])
np.arange(3, 8, 0.5) # Starts at 3, ends at 7.5, gives you numbers in increments of 0.5
array([3. , 3.5, 4. , 4.5, 5. , 5.5, 6. , 6.5, 7. , 7.5])
np.arange(10, 0, -2) # Starts at 10, ends at 2, gives you numbers in increments of -2
array([10,  8,  6,  4,  2])

Slicing#

With NumPy, you can use slicing to extract portions of arrays based on true/false conditions as well as indices.

If choosing to extract portions based on indices, the basic syntax often follows array[start:stop:step] (similar to Python lists). Examples are shown below.

sample_array_appended = np.array([1, 2, 3, 4])
sample_array_appended[:] 
# Using just [:] gives you all the values in the array. You don't have to include the second :
# This happens because the default starting index is 0 and the default ending index is after the last element
# The default step size is 1
array([1, 2, 3, 4])
sample_array_appended[0:1] # Arrays are also 0-indexed, exclusive of the element at the `stop` index
array([1])
sample_array_appended[0:len(sample_array_appended)+1:2] # gives you every other element, starting from the first
array([1, 3])
sample_array_appended[-2:] # gives you the last 2 elements
array([3, 4])

You can also use slicing with matrices (stored as 2D arrays)

first_matrix = np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])
first_matrix[:,:] # You're now slicing along 2 axes (rows and columns), separated by a comma
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
first_matrix[1,:] # Returns the row at row index 1
array([4, 5, 6])
first_matrix[:,1] # Returns the column and column index 1
array([2, 5, 8])
first_matrix[0:2,1:3] # Returns all rows with row indices 0 and 1 and all columns with column indices 1 and 2
array([[2, 3],
       [5, 6]])

Slicing with Booleans#

As mentioned earlier, we can also slice based on true/false conditions. Examples included below.

sample_array_appended = np.array([1, 2, 3, 4])
print(f"The original array is {sample_array_appended}")
print(f"Let's say we want to keep all values less than 3")
print(f"The filtering condition is:  {sample_array_appended < 3}")
print(f"The filtered output is: {sample_array_appended[sample_array_appended < 3]}")
The original array is [1 2 3 4]
Let's say we want to keep all values less than 3
The filtering condition is:  [ True  True False False]
The filtered output is: [1 2]
print(sample_array_appended**2 >= 4) # The output of this is an array of true/false values
sample_array_appended[sample_array_appended**2 >= 4] # We keep all indices in the original array where the value is True
[False  True  True  True]
array([2, 3, 4])