NumPy Exercises Part 1

NumPy Exercises Part 1

In this post, we will be solving 20 exercises in NumPy to sharpen what you have learnt from the NumPy introduction post. If you have not read the NumPy post, I highly encourage to go first through that post on this link and then come back to try out the exercises.

Before we start, please allow me to give you some update about the blog. It is with an honour that I am announcing that MIB has been ranked among the top 40 Machine Learning blog to follow in 2019 alongside some very known Machine Learning blog like Google Machine Learning News, MIT News, Machine Learning Mastery and many more great blogs. To see the full list, please visit this link.

As of the time of writing this post, MIB is just 8 months old and has been ranked already among the top 40 ML blogs. I want to thank all the readers of this blog and Feedspot because you are, indeed my motivation to keep on posting and learning ML. Many of you had asked me how they could support the blog; now you can donate by scrolling to the bottom of the about page here.

I am also taking this occasion to tell you a little bit about Feedspot, Feedspot is for all of us who have busy lives and don’t have the time to check the contents/news from each one of our favourite platforms like blog, RSS, YouTube channel, Podcast, Magazine and many more, Feedspot will take care of all the contents and compile them in one place for you and save you time. Please visit the website at https://www.feedspot.com/.

Now let get back to work; this first part of NumPy exercises posts series is composed of relatively easy exercise compared to part 2. Part 3 will be composed of intermediate and advanced level exercises. These exercises are inspired from this fantastic blog post and I want to give credit to the author of their amazing work, but unfortunately, there are no explanations of the “why” which is the most important thing to understand when trying to grasp a new concept, so I will try to explain in my solutions.

Here is how we will proceed for these series of exercises, I will first post the exercise then you will go and try on your own, only after trying you shall come back and compare your result with mine. Do not cheat ;p

If you can’t solve the challenge after giving all your best, it is ok to come back and check the solution. Don’t feel bad about it; that is how everyone learns, yes! by failling first and figure it out after but most importantly try to understand how the exercise is solved. If you still don’t understand, jump at the comments section below and ask me the question, I will happy to assist you as much as I can.

Note: You will also need to do a little bit of research online for some of the exercises, which is a good thing because googling is an art you must learn and practice as programmer. Use also the handy NumPy’s documentation. An exercise will most likely have more than one way to solve it, as long as the desired output is the same as the solution. You’re good!

Ex 1: Import NumPy and check its version

Q: For our first exercise, we will import NumPy library simple right? also, print its version

Solution

import numpy as np

Before using NumPy anywhere, you must import it.

I hope this one, everyone got it right and you could have called NumPy anything else, but np is what is commonly accepted.

print(np.version.version)
1.16.4

Now to see which version of NumPy we are using, we the version method twice on the NumPy.

Ex 2: Create a one-dimensional NumPy array

Q: Now let’s create a one-dimensional NumPy array from 0 to 9.

Desired output:

# [0 1 2 3 4 5 6 7 8 9]

Solution

my_arr = np.arange(10)
print(my_arr)
[0 1 2 3 4 5 6 7 8 9]

We use the arange method to generate a sequence from 0 to 9.

Ex 3: Create a boolean NumPy array

Q: Let’s create a three-dimensional array composed each of three boolean True value elements (3X3).

Desired output:

# [[ True  True  True]
# [ True  True  True]
# [ True  True  True]]

Solution

my_arr = np.full((3,3),True)
print(my_arr)
[[ True  True  True]
 [ True  True  True]
 [ True  True  True]]

We used the full method and passed in as the first argument, the shape of the array, and as the second argument, the value that will populate the array.

Ex 4: Extract elements in a one-dimension array given specific condition

Q: Let’s extract only even numbers from the following array.

my_arr = np.arange(1,21)
print(my_arr)
[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]

Desired output:

# [ 2  4  6  8 10 12 14 16 18 20]

Solution

even_array = my_arr[my_arr%2==0]
print(even_array)
[ 2  4  6  8 10 12 14 16 18 20]

We use indexing with a condition as the argument. For the condition to be True, it is required that for each element modulo 2 to be equal to 0. Modulo is the remainder(the rest) from the division of each element in the array with the divisor. In our case, the divisor is 2, and if the rest is 0, then we know that it is True because only even numbers divided by 2 return 0. Using this technique, we were able to get back all the even numbers in the array.

Ex 5: Replace elements in a one-dimension array given specific condition

Q: Let’s replace each odd number from the following array with a -1.

my_arr = np.arange(1,21)
print(my_arr)
[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]

Desired output

# [-1  2 -1  4 -1  6 -1  8 -1 10 -1 12 -1 14 -1 16 -1 18 -1 20]

Solution

odd_array = (my_arr%2!=0)
my_arr[odd_array] = -1
print(my_arr)
[-1  2 -1  4 -1  6 -1  8 -1 10 -1 12 -1 14 -1 16 -1 18 -1 20]

We first get a boolean array that returns True if the element is odd or return False if the element is even using modulus by 2. After getting the boolean array, we use indexing on the original array and set it to -1 which will replace all the position where there is a True value by -1.

Ex 6: Replace elements in a one-dimension array given particular condition without affecting the original array

Q: Same question as to the previous one, just that this time the original array should not be changed.

my_arr = np.arange(1,21)
print(my_arr)
[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]

Desired output

# new_arr
# [-1  2 -1  4 -1  6 -1  8 -1 10 -1 12 -1 14 -1 16 -1 18 -1 20]
# my_arr
# [ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]

Solution

1st Method

odd_array = (my_arr%2!=0)
new_array = my_arr.copy()
new_array[odd_array] = -1
print(new_array)
[-1  2 -1  4 -1  6 -1  8 -1 10 -1 12 -1 14 -1 16 -1 18 -1 20]
print(my_arr)
[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]

This example is very similar to the previous one, the only difference is that instead of using the indexing on the original array, we are using a copy of the original array which won’t be affected at all by the changes.

2nd Method

new_array = np.where(my_arr%2 != 0,-1,my_arr)
print(new_array)
[-1  2 -1  4 -1  6 -1  8 -1 10 -1 12 -1 14 -1 16 -1 18 -1 20]

There is a handy where method, which can is more concise than the first method. We first pass the condition, then the value to use where the condition was evaluated to True and finally, the original array.

Ex 7: Reshape an array

Q: Let’s change a one-dimensional array into a two-dimensional array with two rows.

my_arr = np.arange(1,41)
print(my_arr)
[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40]

Desired output

# my_array
# [[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]
# [21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40]]

Solution

1st Method

resh_arr = my_arr.reshape((2,20))
print(resh_arr)
[[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40]]

We have applied the reshape method to the original array, pass in as argument a tuple with a first number being the number of rows and the second number being the number of elements in each row.

Note: Remember that the number of elements must fit in the rows. Here is a formula that can help you reshape an array and avoid errors. The number of elements in 1D = number of row in nD X number of elements in each row.

2nd Method

We can avoid all the hustle of figuring out how to fit the elements in the row with the correct numbers of row and column, we can use just -1 as the second argument in the tuple. Using -1, NumPy will figure out how many elements need to be placed in each array depending on the number of rows.

resh_arr = my_arr.reshape((2,-1))
print(resh_arr)
[[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40]]

Let’s do another example.

resh_arr = my_arr.reshape((4,-1))
print(resh_arr)
[[ 1  2  3  4  5  6  7  8  9 10]
 [11 12 13 14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27 28 29 30]
 [31 32 33 34 35 36 37 38 39 40]]

Using -1 as the second parameter in the tuple, NumPy has figured out that to have a four-dimensional array; each array needs to have 10 elements each.

Ex 8: Stack arrays together vertically

Q: Let’s stack two arrays together vertically (row-wise) to form one array.

my_arr_1 = np.arange(1,21).reshape(2,-1)
my_arr_2 = np.arange(21,41).reshape(2,-1)

Desired output

# [[ 1  2  3  4  5  6  7  8  9 10]
#  [11 12 13 14 15 16 17 18 19 20]
#  [21 22 23 24 25 26 27 28 29 30]
#  [31 32 33 34 35 36 37 38 39 40]]

Solution

1st Method

my_arr_3 = np.vstack((my_arr_1,my_arr_2))
print(my_arr_3)
[[ 1  2  3  4  5  6  7  8  9 10]
 [11 12 13 14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27 28 29 30]
 [31 32 33 34 35 36 37 38 39 40]]

the vstack method stack together arrays vertically.

Note: if two arrays don’t have the same number of columns, they can not stack together vertically.

my_arr_4 = np.arange(1,16).reshape(3,-1)
array([[ 1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10],
       [11, 12, 13, 14, 15]])
my_arr_5 = np.vstack((my_arr_1,my_arr_4))
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-189-f8c619030909> in <module>
----> 1 my_arr_5 = np.vstack((my_arr_1,my_arr_4))


/anaconda3/lib/python3.7/site-packages/numpy/core/shape_base.py in vstack(tup)
    281     """
    282     _warn_for_nonsequence(tup)
--> 283     return _nx.concatenate([atleast_2d(_m) for _m in tup], 0)
    284 
    285 


ValueError: all the input array dimensions except for the concatenation axis must match exactly

my_arr_4 with 5 columns can not be concatenated with my_arr_1 which has 10 columns.

2nd Method

my_arr_3 = np.concatenate((my_arr_1,my_arr_2),axis=0)
print(my_arr_3)
[[ 1  2  3  4  5  6  7  8  9 10]
 [11 12 13 14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27 28 29 30]
 [31 32 33 34 35 36 37 38 39 40]]

We can achieve the same result using the concatenate method; we must pass another argument, which is the axis. If we set the axis to 0, this means that we are stacking the array vertically (row-wise) and if we set it to 1, this means that we are stacking the array horizontally (column-wise).

Ex 9: Stack arrays together horizontally

Q: Let’s stack two arrays together horizontally (column-wise) to form one array.

my_arr_1 = np.arange(1,21).reshape(2,-1)
my_arr_2 = np.arange(21,41).reshape(2,-1)

Desired output

# [[ 1  2  3  4  5  6  7  8  9 10 21 22 23 24 25 26 27 28 29 30]
#  [11 12 13 14 15 16 17 18 19 20 31 32 33 34 35 36 37 38 39 40]]

Solution

1st Method

my_arr_3 = np.hstack((my_arr_1,my_arr_2))
print(my_arr_3)
[[ 1  2  3  4  5  6  7  8  9 10 21 22 23 24 25 26 27 28 29 30]
 [11 12 13 14 15 16 17 18 19 20 31 32 33 34 35 36 37 38 39 40]]

Same as we did with the vstack method, we can do the same using the hstack method this time to stack horizontally.

2nd Method

my_arr_3 = np.concatenate((my_arr_1,my_arr_2),axis=1)
print(my_arr_3)
[[ 1  2  3  4  5  6  7  8  9 10 21 22 23 24 25 26 27 28 29 30]
 [11 12 13 14 15 16 17 18 19 20 31 32 33 34 35 36 37 38 39 40]]

We can use concatenate; we need only to set the axis to 1.

Ex 10: Generate a sequence without hardcoding

Q: Create the following sequence given my_arr array with only numpy methods. No hardcoding.

my_arr = np.array([11,22,33])

Desired output

#[11 11 11 22 22 22 33 33 33 11 22 33 11 22 33]

Solution

trip_each = np.repeat(my_arr,3)
double_array = np.tile(my_arr,2)
final_arr = np.concatenate((trip_each,double_array),axis=0)
print(final_arr)
[11 11 11 22 22 22 33 33 33 11 22 33 11 22 33]

In this example, we have used 3 different methods to achieve the desired result.

First, we used the repeat method to repeat each element in the array. The first argument in this method is the array itself, and the second argument is the number of repeats to be executed then store it in a variable.

Second is the tile method which will duplicate the entire array. The first argument will be the array, and the second will be how many times we repeat the array then store it in another variable.

The third method is the concatenate method, which does a concatenation of trip_each with double_array horizontally by setting the axis to 0 (column-wise).

Ex 11: Get the common elements in arrays

Q: Get common elements in two arrays

array_1 = np.arange(0,31)
array_2 = np.arange(20,51)

Desire output

# [20 21 22 23 24 25 26 27 28 29 30]

Solution

inters_arr = np.intersect1d(array_1,array_2)
print(inters_arr)
[20 21 22 23 24 25 26 27 28 29 30]

The intersect1d method will return a new array composed of elements both present in the first and second array. We stored it in the inters_arr variable then printed it.

Ex 12: Remove the elements that are found in the first arrays but not the second

Q: Delete the elements that are present in the first array but not in the second array. In another world, we want to keep elements in the first array that are not present in the second array.

array_1 = np.arange(0,31)
array_2 = np.arange(20,51)

Desire output

# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]

Solution

array_1 = np.setdiff1d(array_1,array_2)
print(array_1)
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]

the setdiff1d method will return elements present in the first array but not found in the second array. We set it to array_1 and override the original array_1.

Note: If this time, we want elements found in the second array only we inverse the arguments like this

uniq_in_2nd_arr = np.setdiff1d(array_2,array_1)
print(uniq_in_2nd_arr)
[31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50]

Ex 13: Get the elements that are unique in an array

Q: Get only elements that are not duplicated in an array.

my_arr = np.array([5,2,1,2,89,44,5,89,5,90,104,44,88,1])

Desired output

# [  1,   2,   5,  44,  88,  89,  90, 104]

Solution

uniq_arr = np.unique(my_arr)
print(uniq_arr)
[  1   2   5  44  88  89  90 104]

The unique method will return an array composed of only non-repeated elements in a given array.

Ex 14: Get the position where two arrays have common elements

Q: Find all the position where elements in array_1 match the elements in array_2.

array_1 = np.arange(100,121)
array_2 = np.arange(115,151)

Desire output

# (array([15, 16, 17, 18, 19, 20]),)

Solution

1st Method

bool_array = np.in1d(array_1,array_2)
repeat_idx_arr = np.where(bool_array)
print(repeat_idx_arr)
(array([15, 16, 17, 18, 19, 20]),)

For this example, we have used two different NumPy’s methods

The first method is in1d, which will return a boolean array where True on the position where there is a common element in the first and second array.

The second method is the where method which takes the boolean array as an argument and then returns the index position of the True values.

2nd Method

bool_array = np.in1d(array_1,array_2)
repeat_idx_arr = np.nonzero(bool_array)
print(repeat_idx_arr)
(array([15, 16, 17, 18, 19, 20]),)

Here, we achieved the same result using the nonzero method.

Ex 15: Extract all the elements within a given range

Q: Get all the numbers between 50 and 110 in the following randomly generated array.

my_arr = np.random.randint(0,150,size=200)
print(my_arr)
[ 43  74  68  78  47  21  37 112  82  76  75  63 108 115 143 137 100  21
  32 100 105   6  48 110  44 106 113  19  95  81  57  44 137 137  53   2
  67  93 139 114 130  18 118  74  72   8  52  63   4 149 125  39   2  69
 120 111  84   0 100  80   7 112  94  16  20 138  73 145 114  41 145  32
  91  10  51 106  90 128  93 135  67  63  54  86 128  76   7 146 104  83
  88 103  43 134  98  68  90 108  86  30 142  41  30  67 104  81  10  93
  68  82  34  26  40 147  26  89  18 119  15  17  93 145 123  62  32   1
   5  95  22  64   3 111  62  84  29 143  94  31  93 123 149  92 142  10
 112  80  19  77 142  50  78  52  22  16   9  93  56  66  61  69  70  11
  13 119  87 141 102  12  97  66 107  99  51  30  36  78  99  76 115 141
 134  71 112  98  11  85 112 148 119  20  36  19 120  27 137 101  31 149
   3  82]

Desired output

# Can't give the exact array because obviously it is randomly generated. Your array will be most likely different from mine
# We will see how we can fix this in the upcoming exercises.

Solution

1st Method

extract_arr = my_arr[(my_arr >= 50) & (my_arr <=110)]
print(extract_arr)
[ 74  68  78  82  76  75  63 108 100 100 105 110 106  95  81  57  53  67
  93  74  72  52  63  69  84 100  80  94  73  91  51 106  90  93  67  63
  54  86  76 104  83  88 103  98  68  90 108  86  67 104  81  93  68  82
  89  93  62  95  64  62  84  94  93  92  80  77  50  78  52  93  56  66
  61  69  70  87 102  97  66 107  99  51  78  99  76  71  98  85 101  82]

We use indexing on the array and pass in the condition which will only return elements greater or equal to 50.

2nd Method

extract_arr = np.where((my_arr >= 50) & (my_arr <=110))
print(my_arr[extract_arr])
[ 74  68  78  82  76  75  63 108 100 100 105 110 106  95  81  57  53  67
  93  74  72  52  63  69  84 100  80  94  73  91  51 106  90  93  67  63
  54  86  76 104  83  88 103  98  68  90 108  86  67 104  81  93  68  82
  89  93  62  95  64  62  84  94  93  92  80  77  50  78  52  93  56  66
  61  69  70  87 102  97  66 107  99  51  78  99  76  71  98  85 101  82]

Where method will evaluate the condition given to it and then return all the position place where the conditions are met, we stored these positions into a variable then use indexing technique to retrieve the numbers in my_arr.

3rd Method

extract_arr = np.where(np.logical_and((my_arr >= 50),(my_arr <=110)))
print(my_arr[extract_arr])
[ 74  68  78  82  76  75  63 108 100 100 105 110 106  95  81  57  53  67
  93  74  72  52  63  69  84 100  80  94  73  91  51 106  90  93  67  63
  54  86  76 104  83  88 103  98  68  90 108  86  67 104  81  93  68  82
  89  93  62  95  64  62  84  94  93  92  80  77  50  78  52  93  56  66
  61  69  70  87 102  97  66 107  99  51  78  99  76  71  98  85 101  82]

Instead of explicitly use the & symbol, we can use the built-in NumPy equivalent using logical_and method then pass the two condition and use the where method as we did before.

Ex 16: Create a function that compares elements wise two arrays.

Q: Compare the corresponding elements in two arrays and return a new arrays with the maximum number.

array_1 = np.random.randint(0,100,size=10)
array_2 = np.random.randint(0,100,size=10)
print(array_1)
print(array_2)
[44 47 27 22 39 98 55 74 69 79]
[56 53 78 48 12  4 84 50 47 61]
# The function to use
def comparison(x,y):
    if x > y:
        return x
    else:
        return y

Desire output

# Your result will most likely be different because the two arrays are randomly generated
# [56 53 78 48 39 98 84 74 69 79]

Solution

arr_comparisonr = np.vectorize(comparison)
print(arr_comparison(array_1,array_2))
[56 53 78 48 39 98 84 74 69 79]

Think of the vectorize method as the map function of Python but this time for NumPy arrays. This method will take as argument the function which will be applied on all the corresponding elements of the two arrays. Then we store it in a variable which will be used to call the function by passing in the two arrays.

Ex 17: Swap two columns in a two-dimensional array

Q: We want to swap the 4th column with the 7th column in the following array.

my_arr = np.arange(27).reshape(3,-1)
print(my_arr)
[[ 0  1  2  3  4  5  6  7  8]
 [ 9 10 11 12 13 14 15 16 17]
 [18 19 20 21 22 23 24 25 26]]

Desire output

# [[ 0  1  2  6  4  5  3  7  8]
#  [ 9 10 11 15 13 14 12 16 17]
#  [18 19 20 24 22 23 21 25 26]]

Solution

my_arr[:,[6,3]] = my_arr[:,[3,6]]
print(my_arr)
[[ 0  1  2  6  4  5  3  7  8]
 [ 9 10 11 15 13 14 12 16 17]
 [18 19 20 24 22 23 21 25 26]]

We use indexing to swap two columns. A comma separates the two arguments inside the square bracket [ ], the first argument is the row, and we use a colon to get all the elements in the row then the second argument is another square bracket which selects the 7th and 4th columns respectively.

Now comes the following expression on the right of the equal sign, we select all the elements in the row using a colon, and then we select the 4th and 7th column. You see this time we have started with the 4th and then the 7th column.

We have understood what both sides are doing, now comes the magic moment where we set the two sides using the equal sign and keep in mind that the operator precedence of the equal sign is from left to right, so we are changing the 4th column to be the 7th at the same time setting the 7th column to be equal to the 4th. There you go! The swap just happened.

Ex 18: Swap two rows in a two-dimensional array

Q: Now let’s swap the second row with the third row.

my_arr = np.arange(27).reshape(3,-1)
print(my_arr)
[[ 0  1  2  3  4  5  6  7  8]
 [ 9 10 11 12 13 14 15 16 17]
 [18 19 20 21 22 23 24 25 26]]

Desired output

# [[ 0  1  2  3  4  5  6  7  8]
#  [18 19 20 21 22 23 24 25 26]
#  [ 9 10 11 12 13 14 15 16 17]]

Solution

my_arr[[1,2],:] = my_arr[[2,1],:]
print(my_arr)
[[ 0  1  2  3  4  5  6  7  8]
 [18 19 20 21 22 23 24 25 26]
 [ 9 10 11 12 13 14 15 16 17]]

This exercise is almost identical to the previous one; this time, we are executing the same code row-wise instead of column-wise.

Ex 19: Reverse rows in a two-dimensional array

Q: Let’s reverse rows in a two-dimensional array.

my_arr = np.arange(27).reshape(3,-1)
print(my_arr)
[[ 0  1  2  3  4  5  6  7  8]
 [ 9 10 11 12 13 14 15 16 17]
 [18 19 20 21 22 23 24 25 26]]

Desired output

# [[18 19 20 21 22 23 24 25 26]
#  [ 9 10 11 12 13 14 15 16 17]
#  [ 0  1  2  3  4  5  6  7  8]]

Solution

rev_row_arr = np.flip(my_arr,axis=0)
print(rev_row_arr)
[[18 19 20 21 22 23 24 25 26]
 [ 9 10 11 12 13 14 15 16 17]
 [ 0  1  2  3  4  5  6  7  8]]

We used the flip method to reverse the order in the row by setting the axis to 0, which corresponds to the row.

Ex 20: Reverse columns in two-dimensional array

Q: Let’s reverse columns in a two-dimensional array.

my_arr = np.arange(27).reshape(3,-1)
print(my_arr)
[[ 0  1  2  3  4  5  6  7  8]
 [ 9 10 11 12 13 14 15 16 17]
 [18 19 20 21 22 23 24 25 26]]

Desired output

# [[ 8  7  6  5  4  3  2  1  0]
#  [17 16 15 14 13 12 11 10  9]
#  [26 25 24 23 22 21 20 19 18]]

Solution

rev_col_arr = np.flip(my_arr,axis=1)
print(rev_col_arr)
[[ 8  7  6  5  4  3  2  1  0]
 [17 16 15 14 13 12 11 10  9]
 [26 25 24 23 22 21 20 19 18]]

Same as the previous exercise, the only difference is that we have set the axis argument to 1 to refer to the columns.

Conclusion

Hope by now you are starting to gain confidence in your NumPy skills, this is was the first part in a series of three posts entirely dedicated to exercise in NumPy. I am pretty sure that at the end of these series, you will have a strong understanding of NumPy and you start using it in your projects. Stay tuned for part 2!!

Find the jupyter notebook version of this post on my GitHub profile here.

Thank you for doing these exercises with me. I hope you have learned one or two things. If you like this post, please subscribe to stay updated with new posts, and if you have a thought or a question, I would love to hear it by commenting below. Remember keep learning!