Python · Lesson 26

NumPy Array Operations: Splitting, Searching, Sorting, Filtering & Random Numbers

9 phases  ·  Build: Project Overview

👋 Welcome to Lesson 26

Welcome to Lesson 26! In this lesson, you will master five incredibly powerful NumPy skills that data scientists, engineers, and programmers use every single day:

  1. Splitting · breaking one array into multiple smaller arrays
  2. Searching · finding where values live inside an array
  3. Sorting · arranging array elements in order
  4. Filtering · keeping only elements that meet a condition
  5. Random Numbers · generating random integers, floats, and arrays

By the end of this lesson, you will be able to write a realistic mini-project that reads a dataset of student exam scores, cleans it, filters it, sorts it, splits it into groups, and generates random mock data for testing.

Real-world relevance: Every major field · data science, machine learning, finance, science, and engineering · relies on these five operations constantly. If you master them, you can analyse real datasets with confidence.


📚 9 phases🏗️ Project Overview🐍 GitHub Repo
Phase 1 of 9
Lesson Introduction

Welcome to Lesson 26! In this lesson, you will master five incredibly powerful NumPy skills that data scientists, engineers, and programmers use every single day:

  1. Splitting · breaking one array into multiple smaller arrays
  2. Searching · finding where values live inside an array
  3. Sorting · arranging array elements in order
  4. Filtering · keeping only elements that meet a condition
  5. Random Numbers · generating random integers, floats, and arrays

By the end of this lesson, you will be able to write a realistic mini-project that reads a dataset of student exam scores, cleans it, filters it, sorts it, splits it into groups, and generates random mock data for testing.

Real-world relevance: Every major field · data science, machine learning, finance, science, and engineering · relies on these five operations constantly. If you master them, you can analyse real datasets with confidence.


✏️ Your Task
Practise what you just learned about Lesson Introduction. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 2 of 9
Prerequisite Concepts

Before we begin, make sure you are comfortable with these ideas. If any of them are unfamiliar, read the short explanation below before continuing.

What is a NumPy Array?

A NumPy array is a grid of values · like a list, but much more powerful and faster. Every value in the array has a position number called an index, starting from 0.

python
import numpy as np

arr = np.array([10, 20, 30, 40, 50])
#               0    1   2   3   4   ← index numbers

Think of an array like a row of numbered mailboxes. Index 0 is the first mailbox, index 1 is the second, and so on.

What is an Index?

An index is just the position of an element inside an array. It starts at 0, not 1. So the first element is at index 0, the second at index 1, etc.

What is a Boolean?

A boolean is a value that is either True or False. Think of it like a light switch · it is either ON (True) or OFF (False).

Quick Setup · Always Import NumPy First

Every code example in this lesson needs NumPy to be imported. Always start your program with:

python
import numpy as np

✏️ Your Task
Practise what you just learned about Prerequisite Concepts. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 3 of 9
Section 1 · Splitting Arrays

What is Splitting? Why Do We Need It?

Imagine you have a long list of 1,000 exam scores and you want to give each of three teachers a portion of the scores to grade. You would split the big list into three smaller ones. That is exactly what NumPy splitting does.

Splitting is the reverse of joining. Joining merges multiple arrays into one. Splitting breaks one array into multiple parts.

Analogy: Think of a loaf of bread. Joining is baking multiple loaves together. Splitting is slicing one loaf into separate pieces.


The np.array_split() Function

What it does: Splits one array into a given number of smaller arrays.

Syntax:

python
result = np.array_split(array, number_of_splits)
  • array · the array you want to split
  • number_of_splits · how many pieces to make
  • result · a Python list of smaller arrays

Important: The return value is a list of arrays, not a single array!


Example 1 · Split into 3 Equal Parts

python
import numpy as np

arr = np.array([1, 2, 3, 4, 5, 6])

newarr = np.array_split(arr, 3)

print(newarr)

Expected Output:

code
[array([1, 2]), array([3, 4]), array([5, 6])]

Line-by-line explanation:

  • arr = np.array([1, 2, 3, 4, 5, 6]) · creates an array with 6 elements
  • np.array_split(arr, 3) · splits arr into 3 equal pieces: [1,2], [3,4], [5,6]
  • print(newarr) · prints the list of 3 small arrays

Notice: Each piece has exactly 2 elements because 6 ÷ 3 = 2.


Example 2 · Split into 4 Parts (Unequal Division)

What happens when the array cannot be divided equally? NumPy handles this gracefully · it adjusts the sizes from the end.

python
import numpy as np

arr = np.array([1, 2, 3, 4, 5, 6])

newarr = np.array_split(arr, 4)

print(newarr)

Expected Output:

code
[array([1, 2]), array([3, 4]), array([5]), array([6])]

Explanation:

  • 6 elements split into 4 parts: the first two parts get 2 elements, the last two parts get 1 element each.
  • NumPy does NOT crash or complain · it adjusts automatically.

Thinking prompt: What would happen if you tried to split 6 elements into 7 parts? Some parts would be empty! Try it and see.

Important note: The regular np.split() function would crash here if the division is not exactly equal. Always prefer np.array_split() for flexibility.


Accessing Individual Split Pieces

The result of array_split() is a list. You can access each piece using list index notation [0], [1], [2], etc.

python
import numpy as np

arr = np.array([1, 2, 3, 4, 5, 6])

newarr = np.array_split(arr, 3)

print(newarr[0])   # First piece
print(newarr[1])   # Second piece
print(newarr[2])   # Third piece

Expected Output:

code
[1 2]
[3 4]
[5 6]

Splitting 2-D Arrays (Tables)

A 2-D array is like a table with rows and columns. Splitting a 2-D array works by splitting along rows by default.

Example · Split a 2-D Array Into 3 Parts (Row-wise)

python
import numpy as np

arr = np.array([[1, 2],
                [3, 4],
                [5, 6],
                [7, 8],
                [9, 10],
                [11, 12]])

newarr = np.array_split(arr, 3)

print(newarr)

Expected Output:

code
[array([[1, 2],
        [3, 4]]),
 array([[5, 6],
        [7, 8]]),
 array([[ 9, 10],
        [11, 12]])]

Explanation: The 6-row table is split into three 2-row tables.


Example · Split a 2-D Array Along Columns (axis=1)

You can split along columns instead of rows by using axis=1.

python
import numpy as np

arr = np.array([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9],
                [10, 11, 12],
                [13, 14, 15],
                [16, 17, 18]])

newarr = np.array_split(arr, 3, axis=1)

print(newarr)

Expected Output:

code
[array([[ 1],
        [ 4],
        [ 7],
        [10],
        [13],
        [16]]),
 array([[ 2],
        [ 5],
        [ 8],
        [11],
        [14],
        [17]]),
 array([[ 3],
        [ 6],
        [ 9],
        [12],
        [15],
        [18]])]

Explanation:

  • axis=0 means split along rows (default)
  • axis=1 means split along columns
  • The 3-column table is split into three 1-column tables

The hsplit() Shortcut

np.hsplit() is a shorthand for splitting along columns (horizontal split). It is equivalent to array_split(arr, n, axis=1).

python
import numpy as np

arr = np.array([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9],
                [10, 11, 12],
                [13, 14, 15],
                [16, 17, 18]])

newarr = np.hsplit(arr, 3)

print(newarr)

Expected Output: Same as the axis=1 example above.

Tip: Similarly, np.vsplit() splits vertically (same as axis=0), and np.dsplit() splits along the depth axis for 3-D arrays.


✏️ Your Task
> Important note: The regular np.split() function would crash here if the division is not exactly equal. Always prefer np.array_split() for flexibility. ·
Phase 4 of 9
Section 2 · Searching Arrays

What is Searching? Why Do We Need It?

Imagine you have an array of 10,000 temperature readings and you need to find all positions where the temperature exceeded 40°C. You could loop through every element yourself, but NumPy gives you a much faster, cleaner tool: np.where().

Searching means finding the index positions where elements match a condition.


The np.where() Function

What it does: Returns the index positions where a given condition is True.

Syntax:

python
result = np.where(condition)
  • condition · a comparison like arr == 4 or arr > 10
  • result · a tuple containing an array of matching index positions

What is a tuple? A tuple is like a list but surrounded by () instead of []. It cannot be changed after creation.


Example 1 · Find Where a Specific Value Appears

python
import numpy as np

arr = np.array([1, 2, 3, 4, 5, 4, 4])

x = np.where(arr == 4)

print(x)

Expected Output:

code
(array([3, 5, 6]),)

Line-by-line explanation:

  • arr == 4 · checks every element: is this equal to 4?
  • np.where(arr == 4) · returns the positions where the answer is True
  • The value 4 appears at index 3, 5, and 6

Thinking prompt: What does the outer (...) wrapping the result mean? It means np.where returns a tuple. To get just the array inside, you can write x[0].


Example 2 · Find All Odd Numbers

The % symbol is the modulo operator. It gives the remainder after division.

  • 7 % 2 = 1 (because 7 ÷ 2 = 3 remainder 1)
  • 8 % 2 = 0 (because 8 ÷ 2 = 4 remainder 0)
  • If number % 2 == 1, the number is odd
  • If number % 2 == 0, the number is even
python
import numpy as np

arr = np.array([10, 14, 93, 41, 8, 7])

x = np.where(arr % 2 == 1)

print(x)

Expected Output:

code
(array([2, 3, 5]),)

Explanation:

  • Check each element: 10%2=0 (even), 14%2=0 (even), 93%2=1 (odd → index 2), 41%2=1 (odd → index 3), 8%2=0 (even), 7%2=1 (odd → index 5)
  • Odd numbers are at positions 2, 3, and 5

Example 3 · Find All Even Numbers

python
import numpy as np

arr = np.array([10, 14, 93, 41, 8, 7])

x = np.where(arr % 2 == 0)

print(x)

Expected Output:

code
(array([0, 1, 4]),)

Explanation: Even numbers (10, 14, 8) are at index positions 0, 1, and 4.


The np.searchsorted() Function · Binary Search

Sometimes you have a sorted array and you want to know where to insert a new value so the array stays sorted. This is called a binary search.

What is binary search? Instead of checking every element one by one, binary search repeatedly cuts the array in half to find the right position very quickly. It is like looking up a word in a dictionary by opening to the middle first.

Syntax:

python
result = np.searchsorted(sorted_array, value)
  • sorted_array · must already be sorted in ascending order
  • value · the number you want to insert
  • Returns the index where you should insert the value to keep the array sorted

Example 1 · Basic searchsorted

python
import numpy as np

arr = np.array([6, 7, 8, 9])

x = np.searchsorted(arr, 7)

print(x)

Expected Output:

code
1

Explanation:

  • The array is [6, 7, 8, 9]
  • If we insert 7 at index 1, the array stays sorted: [6, 7, 7, 8, 9]
  • The method searches from the left by default, so it finds the first position where 7 is no longer larger than the next value

Example 2 · Search From the Right Side

By default, searchsorted() returns the leftmost valid position. If you want the rightmost position, use side='right'.

python
import numpy as np

arr = np.array([6, 7, 8, 9])

x = np.searchsorted(arr, 7, side='right')

print(x)

Expected Output:

code
2

Explanation:

  • Searching from the right: the value 7 should be inserted at index 2 to keep sorted order: [6, 7, 7, 8, 9]
  • side='left' → insert before existing 7 → index 1
  • side='right' → insert after existing 7 → index 2

Example 3 · Search for Multiple Values at Once

python
import numpy as np

arr = np.array([1, 3, 5, 7])

x = np.searchsorted(arr, [2, 4, 6])

print(x)

Expected Output:

code
[1 2 3]

Explanation:

  • 2 would go at index 1 (between 1 and 3)
  • 4 would go at index 2 (between 3 and 5)
  • 6 would go at index 3 (between 5 and 7)

Real-world use: Binary search with searchsorted is used in stock market order books, database lookups, and anywhere you need to find where to insert a value quickly in a sorted list.


✏️ Your Task
Practise what you just learned about Searching Arrays. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 5 of 9
Section 3 · Sorting Arrays

What is Sorting? Why Do We Need It?

Sorting means arranging elements in a specific order · usually from smallest to largest (ascending) or largest to smallest (descending). We sort data constantly in real life: ranking student scores, ordering products by price, alphabetising names.


The np.sort() Function

What it does: Returns a sorted copy of the array, leaving the original unchanged.

Syntax:

python
sorted_arr = np.sort(array)

Important: np.sort() does NOT change the original array. It creates a new sorted copy. This is safe behaviour · your original data is preserved.


Example 1 · Sort Numbers

python
import numpy as np

arr = np.array([3, 2, 0, 1])

print(np.sort(arr))

Expected Output:

code
[0 1 2 3]

Explanation: The numbers are rearranged from smallest to largest.

Thinking prompt: What happens to the original arr after sorting? It stays unchanged! Try printing arr after the sort to confirm.


Example 2 · Sort Strings Alphabetically

NumPy can also sort arrays of text (strings)! It sorts them alphabetically, just like a dictionary.

python
import numpy as np

arr = np.array(['banana', 'cherry', 'apple'])

print(np.sort(arr))

Expected Output:

code
['apple' 'banana' 'cherry']

Explanation: 'apple' comes first alphabetically, then 'banana', then 'cherry'.


Example 3 · Sort Booleans

python
import numpy as np

arr = np.array([True, False, True])

print(np.sort(arr))

Expected Output:

code
[False  True  True]

Explanation: In Python/NumPy, False is treated as 0 and True as 1. So False sorts before True.


Sorting a 2-D Array

When you sort a 2-D array, NumPy sorts each row independently.

python
import numpy as np

arr = np.array([[3, 2, 4],
                [5, 0, 1]])

print(np.sort(arr))

Expected Output:

code
[[2 3 4]
 [0 1 5]]

Explanation:

  • Row 1: [3, 2, 4] → sorted to [2, 3, 4]
  • Row 2: [5, 0, 1] → sorted to [0, 1, 5]
  • Each row is sorted separately

Thinking prompt: What if you wanted to sort the columns instead of the rows? You could use np.sort(arr, axis=0). Try it!


How to Sort in Descending Order (Largest First)

np.sort() only sorts in ascending order by default. To sort in descending order, sort first, then reverse using [::-1].

python
import numpy as np

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

sorted_desc = np.sort(arr)[::-1]

print(sorted_desc)

Expected Output:

code
[9 6 5 4 3 2 1 1]

Explanation:

  • np.sort(arr) gives [1, 1, 2, 3, 4, 5, 6, 9]
  • [::-1] reverses it to [9, 6, 5, 4, 3, 2, 1, 1]

✏️ Your Task
Practise what you just learned about Sorting Arrays. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 6 of 9
Section 4 · Filtering Arrays

What is Filtering? Why Do We Need It?

Filtering means selecting only the elements that meet a condition and creating a new array with just those elements.

Think of it like a coffee filter: you pour in a mixture, and only what passes through the filter gets collected. In NumPy, only elements that pass your condition get collected.

Real-world examples:

  • Keep only exam scores above 50 (passing scores)
  • Keep only temperatures below 0°C (freezing days)
  • Keep only even product IDs
  • Keep only positive financial transactions

What is a Boolean Index List?

A boolean index list is a list of True and False values, one for each element in an array. When you use it to index an array:

  • Elements with Trueincluded in the result
  • Elements with Falseexcluded from the result

Example 1 · Manual Boolean Filter (Hard-coded)

python
import numpy as np

arr = np.array([41, 42, 43, 44])

x = [True, False, True, False]

newarr = arr[x]

print(newarr)

Expected Output:

code
[41 43]

Line-by-line explanation:

  • arr = [41, 42, 43, 44] · four elements at index 0, 1, 2, 3
  • x = [True, False, True, False] · index 0 = True, index 1 = False, index 2 = True, index 3 = False
  • arr[x] · keep index 0 (41 ✓), skip index 1 (42 ✗), keep index 2 (43 ✓), skip index 3 (44 ✗)
  • Result: [41, 43]

Thinking prompt: Why was 42 excluded? Because the value at its position in the filter list was False.


Example 2 · Building a Filter with a Loop

In practice, you almost never hard-code True and False. Instead, you build the filter automatically using a loop.

Scenario: Keep only values higher than 42.

python
import numpy as np

arr = np.array([41, 42, 43, 44])

# Step 1: Create an empty list to hold True/False values
filter_arr = []

# Step 2: Go through each element in arr
for element in arr:
    # Step 3: If the element is higher than 42, add True; otherwise add False
    if element > 42:
        filter_arr.append(True)
    else:
        filter_arr.append(False)

# Step 4: Apply the filter to the array
newarr = arr[filter_arr]

print(filter_arr)
print(newarr)

Expected Output:

code
[False, False, True, True]
[43 44]

Line-by-line explanation:

  • We loop through [41, 42, 43, 44]
  • 41 > 42? No → False
  • 42 > 42? No → False
  • 43 > 42? Yes → True
  • 44 > 42? Yes → True
  • filter_arr = [False, False, True, True]
  • arr[filter_arr] keeps only elements at True positions → [43, 44]

Example 3 · Loop Filter for Even Numbers

python
import numpy as np

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

filter_arr = []

for element in arr:
    if element % 2 == 0:    # Is the remainder zero? (even number?)
        filter_arr.append(True)
    else:
        filter_arr.append(False)

newarr = arr[filter_arr]

print(filter_arr)
print(newarr)

Expected Output:

code
[False, True, False, True, False, True, False]
[2 4 6]

Example 4 · The NumPy Shortcut: Direct Array Conditions ⭐

The loop method works but is long. NumPy provides a much shorter and more elegant way: you can write the condition directly on the array, and NumPy creates the boolean array automatically!

Filtering values > 42:

python
import numpy as np

arr = np.array([41, 42, 43, 44])

filter_arr = arr > 42

newarr = arr[filter_arr]

print(filter_arr)
print(newarr)

Expected Output:

code
[False False  True  True]
[43 44]

What happened? arr > 42 does NOT give a single True/False. It checks every element and returns a boolean array: [False, False, True, True]. This is called vectorised comparison · NumPy processes all elements at once, which is much faster than a Python loop.


Example 5 · Direct Filter for Even Numbers (Short Version)

python
import numpy as np

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

filter_arr = arr % 2 == 0

newarr = arr[filter_arr]

print(filter_arr)
print(newarr)

Expected Output:

code
[False  True False  True False  True False]
[2 4 6]

One-Line Filtering

You can even combine the filter and the indexing into a single line:

python
import numpy as np

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

newarr = arr[arr % 2 == 0]

print(newarr)

Expected Output:

code
[2 4 6]

This is the most common style used by professional data scientists.


✏️ Your Task
Practise what you just learned about Filtering Arrays. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 7 of 9
Section 5 · Random Numbers in NumPy

What is a Random Number?

A random number is a number that cannot be predicted logically before it is generated. This is different from simply "a number that changes" · true randomness means there is no pattern.

Pseudo-random vs True Random:

  • Pseudo-random: Generated by a mathematical algorithm. It looks random but is technically predictable if you know the algorithm and starting point (called a "seed"). This is what NumPy uses.
  • True random: Generated from unpredictable physical sources like keyboard timings, mouse movements, or atmospheric noise.

For most purposes · simulations, testing, data science, games · pseudo-random numbers are perfectly fine.


Importing the Random Module

python
from numpy import random

Or you can use np.random after importing NumPy:

python
import numpy as np
# then use: np.random.randint(...)

Both approaches work. In this lesson, we use from numpy import random to keep examples concise.


Generating a Single Random Integer

random.randint(n) returns a random integer from 0 up to but not including n.

python
from numpy import random

x = random.randint(100)

print(x)

Expected Output: A random integer between 0 and 99 (e.g., 47)

Note: Your output will be different each time you run this! That is the whole point.

Real-world analogy: This is like rolling a 100-sided die and reading the number.


Generating a Single Random Float

random.rand() returns a random decimal number between 0.0 and 1.0 (never exactly 1.0).

python
from numpy import random

x = random.rand()

print(x)

Expected Output: A random float like 0.5488135039273248


Generating a 1-D Array of Random Integers

Use randint(high, size=(n)) to generate an array of n random integers from 0 to high - 1.

python
from numpy import random

x = random.randint(100, size=(5))

print(x)

Expected Output: 5 random integers, e.g., [51 92 14 71 60]


Generating a 2-D Array of Random Integers

Use size=(rows, columns) to make a 2-D array.

python
from numpy import random

x = random.randint(100, size=(3, 5))

print(x)

Expected Output: A 3-row, 5-column table of random integers, e.g.:

code
[[44 38 69 21  5]
 [81 63 27 72  8]
 [96  3 44 56 78]]

Generating a 1-D Array of Random Floats

rand(n) generates n random floats between 0.0 and 1.0.

python
from numpy import random

x = random.rand(5)

print(x)

Expected Output: 5 random decimals, e.g., [0.548 0.715 0.603 0.545 0.424]


Generating a 2-D Array of Random Floats

python
from numpy import random

x = random.rand(3, 5)

print(x)

Expected Output: A 3-row, 5-column table of random floats between 0 and 1.


Generating Random Values from a Custom List: choice()

random.choice() picks a random value from an array you provide.

python
from numpy import random

x = random.choice([3, 5, 7, 9])

print(x)

Expected Output: One of: 3, 5, 7, or 9 (randomly chosen)


Generating a 2-D Array from a Custom List

python
from numpy import random

x = random.choice([3, 5, 7, 9], size=(3, 5))

print(x)

Expected Output: A 3×5 grid where every value is one of 3, 5, 7, or 9, e.g.:

code
[[9 5 3 7 9]
 [5 5 7 3 9]
 [7 5 5 7 9]]

Quick Summary of Random Functions

FunctionWhat it doesExample
random.randint(n)One random integer from 0 to n-1random.randint(10)7
random.randint(n, size=(k))Array of k random integersrandom.randint(10, size=(5))
random.randint(n, size=(r, c))2-D array of random integersrandom.randint(10, size=(3, 4))
random.rand()One random float between 0.0 and 1.0random.rand()0.73
random.rand(k)Array of k random floatsrandom.rand(5)
random.rand(r, c)2-D array of random floatsrandom.rand(3, 5)
random.choice(arr)One random value from arrrandom.choice([1,2,3])2
random.choice(arr, size=(r, c))2-D array from arr's valuesrandom.choice([1,2,3], size=(2,3))

✏️ Your Task
Practise what you just learned about Random Numbers in NumPy. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 8 of 9
Guided Practice Exercises
🎯 Your Challenge

Exercise 1 · Splitting a Sensor Dataset

Objective: Practice splitting arrays into equal parts.

Scenario: You have 9 temperature readings from 3 weather stations. Split the data so each station gets its own set of readings.

Steps:

  1. Create the array: [22, 25, 19, 30, 28, 24, 15, 18, 21]
  2. Split it into 3 equal parts
  3. Print each part separately

Code:

✏️ Task
Practise what you just learned about Guided Practice Exercises. Open your editor, type the examples above by hand, modify them, and observe what changes.
python
import numpy as np

temperatures = np.array([22, 25, 19, 30, 28, 24, 15, 18, 21])

parts = np.array_split(temperatures, 3)

print("Station 1:", parts[0])
print("Station 2:", parts[1])
print("Station 3:", parts[2])

Expected Output:

code
Station 1: [22 25 19]
Station 2: [30 28 24]
Station 3: [15 18 21]

Self-check: How many elements should each station have? What happens if you split 9 elements into 4 groups?


Exercise 2 · Finding Passing Scores

Objective: Practice using np.where() to find index positions.

Scenario: You have 7 student exam scores. Find which students passed (scored 50 or above).

Steps:

  1. Create the scores array: [45, 72, 38, 91, 55, 49, 83]
  2. Use np.where() to find positions where score >= 50
  3. Print the positions
  4. Print the actual passing scores

Code:

python
import numpy as np

scores = np.array([45, 72, 38, 91, 55, 49, 83])

pass_positions = np.where(scores >= 50)

print("Positions of passing students:", pass_positions)
print("Passing scores:", scores[pass_positions])

Expected Output:

code
Positions of passing students: (array([1, 3, 4, 6]),)
Passing scores: [72 91 55 83]

Self-check: How many students passed? What score is at position 3?


Exercise 3 · Sorting a Leaderboard

Objective: Practice sorting arrays.

Scenario: Sort student scores to build a leaderboard (highest first).

Code:

python
import numpy as np

scores = np.array([45, 72, 38, 91, 55, 49, 83])

ascending = np.sort(scores)
descending = np.sort(scores)[::-1]

print("Sorted (low to high):", ascending)
print("Leaderboard (high to low):", descending)

Expected Output:

code
Sorted (low to high): [38 45 49 55 72 83 91]
Leaderboard (high to low): [91 83 72 55 49 45 38]

Exercise 4 · Filtering Scores Above Average

Objective: Practice direct boolean filtering.

Scenario: Find and display only scores that are above the average.

Code:

python
import numpy as np

scores = np.array([45, 72, 38, 91, 55, 49, 83])

average = np.mean(scores)  # np.mean() calculates the average
print("Average score:", average)

above_average = scores[scores > average]
print("Above-average scores:", above_average)

Expected Output:

code
Average score: 61.857142857142854
Above-average scores: [72 91 83]

Exercise 5 · Generating Mock Exam Data

Objective: Practice using random to generate test data.

Scenario: Generate a random dataset of 20 student exam scores (between 0 and 100) for testing your code.

Code:

python
from numpy import random
import numpy as np

mock_scores = random.randint(101, size=(20))

print("Mock scores:", mock_scores)
print("Highest:", np.max(mock_scores))
print("Lowest:", np.min(mock_scores))
print("Average:", np.mean(mock_scores).round(2))

Expected Output (yours will differ):

code
Mock scores: [51 92 14 71 60 20 82 86 74 74 87 99 23  2 21 52  1 87 29 37]
Highest: 99
Lowest: 1
Average: 53.15

Phase 9 of 9
Common Beginner Mistakes

Mistake 1 · Using np.split() Instead of np.array_split()

python
# WRONG - this will crash if division is unequal
arr = np.array([1, 2, 3, 4, 5])
# np.split(arr, 3)  # ValueError! 5 cannot be split into 3 equal parts

# RIGHT - this adjusts automatically
result = np.array_split(arr, 3)   # Works fine: [1,2], [3,4], [5]

Mistake 2 · Forgetting That where() Returns a Tuple

python
import numpy as np
arr = np.array([1, 2, 3, 4])

result = np.where(arr > 2)
print(result)        # (array([2, 3]),)  ← it's a tuple!
print(result[0])     # array([2, 3])     ← to get just the array, use [0]

Mistake 3 · Assuming np.sort() Modifies the Original Array

python
import numpy as np
arr = np.array([3, 1, 2])

np.sort(arr)       # This does NOT change arr!
print(arr)         # Still [3 1 2] ← ORIGINAL UNCHANGED

sorted_arr = np.sort(arr)   # You must capture the result in a variable
print(sorted_arr)  # [1 2 3]

Mistake 4 · Building Filter Lists Manually When You Can Use Direct Conditions

python
import numpy as np
arr = np.array([10, 20, 30, 40, 50])

# SLOW and unnecessary
filter_arr = []
for element in arr:
    if element > 25:
        filter_arr.append(True)
    else:
        filter_arr.append(False)
result = arr[filter_arr]

# FAST and Pythonic - do this instead!
result = arr[arr > 25]     # One line, faster, cleaner
print(result)              # [30 40 50]

Mistake 5 · Using searchsorted() on an Unsorted Array

python
import numpy as np

# WRONG - array is not sorted!
arr = np.array([5, 2, 8, 1, 9])
# np.searchsorted(arr, 6)  # The result would be meaningless!

# RIGHT - sort first
arr_sorted = np.sort(arr)       # [1 2 5 8 9]
position = np.searchsorted(arr_sorted, 6)
print(position)                  # 3 (insert 6 between 5 and 8)

Mistake 6 · Confusing randint() and rand()

python
from numpy import random

# randint → random INTEGERS
x = random.randint(10)         # One integer from 0 to 9
y = random.randint(10, size=5) # Five integers from 0 to 9

# rand → random FLOATS between 0.0 and 1.0
a = random.rand()              # One float
b = random.rand(5)             # Five floats

✏️ Your Task
Practise what you just learned about Common Beginner Mistakes. Open your editor, type the examples above by hand, modify them, and observe what changes.
🏗️ Build It — Mini Project
Project Overview

This project combines all five skills from this lesson into a realistic data analysis tool.

Project Overview

You are a teacher. You have a class of 12 students. You want to:

  1. Generate mock scores (using random)
  2. Sort the scores (from highest to lowest)
  3. Filter out failing scores (below 50)
  4. Find where specific score thresholds occur
  5. Split the class into three performance groups

Stage 1 · Setup: Generate Student Scores

starter.py
from numpy import random
import numpy as np

# Set a seed so we get the same "random" results each run (useful for testing)
random.seed(42)

# Generate 12 scores between 30 and 100
scores = random.randint(30, 101, size=(12))
student_ids = np.array([f"S{i+1:02d}" for i in range(12)])

print("=== RAW SCORES ===")
for sid, score in zip(student_ids, scores):
    print(f"  {sid}: {score}")

Lesson 26 complete! 🎉

You covered: