Problem Set 3: NumPy

Homework for Module 3

Introduction

These exercises collect the former NumPy review and quiz material in the sessions where each concept is taught. Try each exercise before opening its collapsed solution.

There are currently no mandatory exercises in this problem set, so every exercise includes a solution. If mandatory exercises are added later, they should use red warning callouts and omit the solution.

import numpy as np
ModuleNotFoundError: No module named 'numpy'

Session 11: Introduction to NumPy

Shadowing the NumPy Alias

Predict what happens when this code runs. Why does np.array() fail?

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

The assignment np = 4 replaces the NumPy alias with an integer. An integer has no array attribute, so Python raises an AttributeError. Restore the alias before using NumPy again:

import numpy as np
arr = np.array([2, 3, 4, 5])
print(arr)
ModuleNotFoundError: No module named 'numpy'
Replacing a Sum Loop

Which NumPy expression replaces the loop and returns one total?

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

total = 0
for value in arr:
    total = total + value
NameError: name 'np' is not defined

Choose among np.cumsum(arr), np.sum(arr), and np.loop(arr).

total = np.sum(arr)
print(total)
NameError: name 'np' is not defined

np.cumsum(arr) returns every running total, while np.sum(arr) returns one value. NumPy has no np.loop() function.

Array Dimensions

Which expression reports the number of dimensions of this array?

arr = np.array([[1, 2, 3], [4, 5, 6]])
NameError: name 'np' is not defined

Choose among arr.shape, arr.ndim, arr.ndim(), and np.arr.ndim.

arr.ndim is the integer 2. It is a property, so it does not use parentheses. arr.shape is (2, 3) and answers a different question.

Image Shape

A color image uses a three-dimensional array. Which assignment retrieves its height and width without storing the color-channel count?

img = np.array([
    [[1, 2, 3], [4, 5, 6]],
    [[7, 8, 9], [10, 11, 12]]
])
NameError: name 'np' is not defined
height, width, _ = img.shape
print(height, width)
NameError: name 'img' is not defined

The shape is (height, width, channels). The underscore records that the third value is intentionally unused.

Empty Arrays

Which expressions correctly create an empty NumPy array?

# Option A
# x = np.array()

# Option B
x = np.array([])

# Option C
y = np.zeros(0, dtype=int)
NameError: name 'np' is not defined

Options B and C work and produce arrays with shape (0,). Option A fails because np.array() requires an object argument.

Array-Creation Debugging

Determine which lines work. For each valid line, give the output shape and data type.

a = np.zeros(3).astype(float)
c = np.ones(5, dtype=bool)
d = np.ones((5, 5))

# Deliberately incorrect
# b = np.zeros(3, 3)
NameError: name 'np' is not defined
  • a has shape (3,) and a floating-point dtype.
  • c has shape (5,) and Boolean dtype.
  • d has shape (5, 5) and a floating-point dtype.
  • np.zeros(3, 3) is invalid because the shape must be supplied as one tuple for a 2D array: np.zeros((3, 3)).

Session 12: Array Arithmetic and Broadcasting

Matrix Multiplication

Which expressions perform matrix multiplication for these two-dimensional arrays?

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
NameError: name 'np' is not defined

Choose among a * b, a.b, a @ b, and np.matmul(a, b).

Both a @ b and np.matmul(a, b) perform matrix multiplication. The expression a * b multiplies element by element, while a.b is not valid syntax for this operation.

Assigning One Value to a Row

Predict the final value of x.

x = np.array([[1, 2], [3, 4]])
x[0] = 0
print(x)
NameError: name 'np' is not defined

The scalar 0 broadcasts across the first row:

[[0 0]
 [3 4]]
Fixing Vector Addition

The following code should add [1, 2, 3] and [4, 5, 6]. Fix both array-creation errors.

x = np.array(1, 2, 3)
y = np.arange(4, 6, 1)
print(x + y)
x = np.array([1, 2, 3])
y = np.arange(4, 7)
print(x + y)
NameError: name 'np' is not defined

np.array() receives one collection, and the stopping value of np.arange() is excluded.

Broadcasting a Row Vector

Predict the shape and values of c.

a = np.array([1, 2, 3])
b = np.array([[4, 5, 6], [7, 8, 9]])
c = b > a
NameError: name 'np' is not defined

The shape is (2, 3). NumPy compares a with every row of b:

[[ True  True  True]
 [ True  True  True]]
Incompatible Shapes

Explain why this comparison fails.

a = np.array([1, 2, 3])
b = np.array([4, 5, 6, 7])
c = b > a

The trailing dimensions are 4 and 3. They are neither equal nor 1, so the arrays are not broadcast-compatible.

Row and Column Assignment

Predict the two outputs.

rows = np.array([[1, 2], [3, 4]])
rows[1, :] = 0

columns = np.array([[1, 2], [3, 4]])
columns[:, 0] = 0

print(rows)
print(columns)
NameError: name 'np' is not defined
[[1 2]
 [0 0]]

[[0 2]
 [0 4]]

The colon selects every position along the corresponding axis.


Session 13: Loading and Saving Data

Text and Binary Formats

Write code that saves the array below once as readable text and once in NumPy’s binary format. Then load both files and compare their shapes.

measurements = np.array([[1.2, 1.4], [1.8, 2.1]])
NameError: name 'np' is not defined
np.savetxt("measurements.txt", measurements)
np.save("measurements.npy", measurements)

from_text = np.loadtxt("measurements.txt")
from_binary = np.load("measurements.npy")

print(from_text.shape)
print(from_binary.shape)

Both loaded arrays have shape (2, 2). The .npy format also preserves NumPy-specific dtype and shape information exactly.


Session 14: Array Manipulation

Joining Two Vectors

Join a and b into one one-dimensional array.

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
NameError: name 'np' is not defined
c = np.concatenate([a, b])
print(c)
NameError: name 'np' is not defined

Adding the arrays would produce [5, 7, 9]; it would not join them.

Predicting a Reshape

What are the shape and values of reshaped?

arr = np.array([1, 2, 3, 4, 5, 6])
reshaped = arr.reshape(2, 3)
NameError: name 'np' is not defined

The result has two rows and three columns:

[[1 2 3]
 [4 5 6]]
Vertical Stacking

Stack a = [1, 2, 3] and b = [4, 5, 6] as two rows of one matrix.

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
stacked = np.vstack([a, b])
print(stacked)
NameError: name 'np' is not defined

The result has shape (2, 3).

Sliding Windows

Fix the code so it creates

[[ 1,  2,  3,  4],
 [ 2,  3,  4,  5],
 ...
 [11, 12, 13, 14]]

from z = np.arange(1, 15).

z = np.arange(1, 15)
windows = []

for index in range(1, len(z) - 3):
    windows.append(z[index:index + 3])

result = np.array(windows)
NameError: name 'np' is not defined
z = np.arange(1, 15)
windows = []

for index in range(len(z) - 3):
    windows.append(z[index:index + 4])

result = np.array(windows)
print(result)
NameError: name 'np' is not defined

The first window starts at index 0, and each slice must contain four values.

reshape() Does Not Modify in Place

Fix the code so it creates the requested matrix.

x = np.linspace(1, 9, 9)
x.reshape((3, 3))
x[1, 1] = 0
x = np.linspace(1, 9, 9)
x = x.reshape((3, 3))
x[1, 1] = 0
print(x)
NameError: name 'np' is not defined

reshape() returns the reshaped array. Assign that return value to a variable before using two indices.

Fibonacci Array Debugging

The function should return the first n Fibonacci values as an integer array. Fix the initialization and return statement.

def fibonacci_sequence(n: int):
    arr = np.array()

    for index in range(n):
        if index <= 1:
            new_element = 1
        else:
            new_element = arr[-2] + arr[-1]
        arr = np.append(arr, new_element)

    return int(arr)
def fibonacci_sequence(n: int) -> np.ndarray:
    arr = np.array([], dtype=int)

    for index in range(n):
        if index <= 1:
            new_element = 1
        else:
            new_element = arr[-2] + arr[-1]
        arr = np.append(arr, new_element)

    return arr


print(fibonacci_sequence(10))
NameError: name 'np' is not defined

np.array([]) creates an empty array; int(arr) incorrectly tries to turn the complete array into one integer.

Concatenating an Empty Array

Predict the output.

x = np.array([])
y = np.zeros(3)
a = np.concatenate([x, y])
print(a)
NameError: name 'np' is not defined

Python prints [0. 0. 0.]. Concatenating an empty array contributes no elements, and np.zeros(3) uses a floating-point dtype by default.


Session 15: Masking

Mask Length

Explain why this code fails, then repair it.

arr = np.arange(4)
arr_bool = np.array([True, False, True, False, True])
selected = arr[arr_bool]

A Boolean mask must have the same length as the axis it selects. Either extend arr to five elements or shorten the mask:

arr = np.arange(4)
arr_bool = np.array([True, False, True, False])
selected = arr[arr_bool]
print(selected)
NameError: name 'np' is not defined
Filtering Values

Without a loop, select every value less than or equal to 3.

arr = np.array([1, 2, 3, 4, 5])
NameError: name 'np' is not defined
result = arr[arr <= 3]
print(result)
NameError: name 'arr' is not defined
Selecting Even Values

Predict the output.

arr = np.array([1, 2, 3, 4, 5])
mask = arr % 2 == 0
print(arr[mask])
NameError: name 'np' is not defined

Python prints [2 4]. The mask is True exactly where the remainder after division by 2 is zero.

Replacing Selected Values

Replace every value greater than 3 with 10.

arr = np.array([1, 2, 3, 4, 5])
NameError: name 'np' is not defined
arr[arr > 3] = 10
print(arr)
NameError: name 'arr' is not defined

Assigning to a loop variable would only rebind that variable; it would not modify the array.

Selecting Odd Values

Write one Boolean expression that selects all odd values from the array.

arr = np.array([1, 2, 3, 4, 5, 6])
NameError: name 'np' is not defined
odds = arr[arr % 2 != 0]
print(odds)
NameError: name 'arr' is not defined
Combining Two Conditions

The code should return the positive odd values [1, 3], but its second mask has the wrong length. Fix it.

arr = np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4])
mask_odd = arr % 2 != 0
mask_positive = arr[mask_odd] > 0
result = arr[mask_positive]

Build both masks from the original array and combine them element by element:

arr = np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4])
mask_odd = arr % 2 != 0
mask_positive = arr > 0
result = arr[mask_odd & mask_positive]
print(result)
NameError: name 'np' is not defined
Mixed Data Types

Why can this array not be compared directly with the integer 2?

arr = np.array(["a", 1, "b", 2, "c", 3, "d", 4])
result = arr[arr > 2]

NumPy chooses one dtype for the complete array. Because strings are present, all values become strings, so comparing them with an integer is invalid. If mixed Python types are required, keep a list and test each element explicitly:

values = ["a", 1, "b", 2, "c", 3, "d", 4]
result = [value for value in values if type(value) is int and value > 2]
print(result)
[3, 4]
Three Ways to Select Positions

Predict all three outputs and explain why the first differs from the other two.

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

print(arr[1::2])
print(arr[arr % 2 != 0])
print(arr[(arr.astype(int) / 2) != (arr / 2)])
NameError: name 'np' is not defined

The outputs are [2 4], [1 3 5], and [1 3 5]. The first expression selects odd indices. The other expressions select odd values, although arr % 2 != 0 is much clearer than comparing two division results.


Session 16: Random Numbers and Fancy Indexing

A 50% Event

Complete the condition so "Welcome" is printed with probability 0.5.

p = np.random.uniform(0, 100)

if ...:
    print("Welcome")
p = np.random.uniform(0, 100)

if p >= 50:
    print("Welcome")
NameError: name 'np' is not defined

A continuous uniform sample is equally likely to fall below or above 50.

Simulating Dice Rolls

Fix the code so each player receives an integer from 1 through 6.

players = ["A", "B", "C"]
results = np.random.uniform(1, 6, size=3)
NameError: name 'np' is not defined
players = ["A", "B", "C"]
results = np.random.randint(1, 7, size=len(players))
player_results = dict(zip(players, results))
print(player_results)
NameError: name 'np' is not defined

The upper bound 7 is excluded, so the possible results are 1 through 6.

Replacing the First and Last Values

Fix the indexing so both positions become 100 in one assignment.

arr = np.array([1, 2, 3, 4, 5])
arr[0, -1] = 100
arr = np.array([1, 2, 3, 4, 5])
arr[[0, -1]] = 100
print(arr)
NameError: name 'np' is not defined

The outer brackets create one array of fancy indices for the one-dimensional array.

Repeated Fancy Indices

Predict the output.

arr = np.array([1, 2, 3, 4, 5])
print(arr[[0, 0, 1]])
NameError: name 'np' is not defined

Python prints [1 1 2]. Fancy indices may repeat, and the output preserves their order.

Correcting Row and Column Indices

Fix the code so it extracts the third row and second column.

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

third_row = matrix[3, :]
second_column = matrix[:, 2]
matrix = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])
third_row = matrix[2, :]
second_column = matrix[:, 1]

print(third_row)
print(second_column)
NameError: name 'np' is not defined

Python indices begin at zero.

Extracting a Diagonal

Use fancy indexing to extract the diagonal of this matrix.

matrix = np.array([
    [10, 20, 30, 40],
    [50, 60, 70, 80],
    [90, 100, 110, 120],
    [130, 140, 150, 160]
])
NameError: name 'np' is not defined
indices = np.arange(matrix.shape[0])
diagonal = matrix[indices, indices]
print(diagonal)
NameError: name 'np' is not defined

np.diag(matrix) is also valid, but the paired indices make the fancy-indexing mechanism visible.

Reversing an Array

Fix the slice so it reverses this one-dimensional array.

arr = np.array([1, 2, 3, 4, 5])
reversed_arr = arr[::-1, :]
arr = np.array([1, 2, 3, 4, 5])
reversed_arr = arr[::-1]
print(reversed_arr)
NameError: name 'np' is not defined

A one-dimensional array has only one axis, so a second slice is invalid.

Top-Left Subarray

Extract the top-left 2 × 2 block.

matrix = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])
NameError: name 'np' is not defined
subarray = matrix[0:2, 0:2]
print(subarray)
NameError: name 'matrix' is not defined
Random Seeds and Reassignment

Explain what the seed controls and what value type x has at the end.

x = 10
np.random.seed(x)
x = np.random.randint(low=0, high=1000)
print(x)
NameError: name 'np' is not defined

The seed fixes the pseudo-random sequence, so rerunning the code with the same NumPy random generator produces the same result. The final assignment replaces x = 10 with one NumPy integer sampled from 0 through 999.

Monty Hall Columns

The value 1 marks the winning door in each game. Select door number 2 from every game and compute the overall proportion of winning entries.

games = np.array([
    [0, 1, 0],
    [1, 0, 0],
    [0, 1, 0]
])
NameError: name 'np' is not defined
door_two = games[:, 1]
gain_rate = np.mean(games)

print(door_two)
print(gain_rate)
NameError: name 'games' is not defined

Door number 2 uses index 1. Because each row contains one car, one third of all entries are winning entries.


Session 17: Statistics and Simulation

Row Means

Fix the code so it prints [2, 5, 8].

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

mean = int(np.mean(arr))
print(mean)
NameError: name 'np' is not defined
row_means = np.mean(arr, axis=1).astype(int)
print(row_means)
NameError: name 'np' is not defined

Reducing axis 1 combines the values across each row.

Means Along Every Axis

Fix the loop so it stores the mean along axis 0 and axis 1 in a dictionary.

arr = np.array([[1, 2], [1, 3], [1, 7]])
axes = arr.shape
dict_means = {}

for axis in axes:
    key = f"mean_{axis}"
    value = np.mean(arr, axis=axis)
    dict_stats.append({key: value})
arr = np.array([[1, 2], [1, 3], [1, 7]])
dict_means = {}

for axis in range(arr.ndim):
    dict_means[f"mean_{axis}"] = np.mean(arr, axis=axis)

print(dict_means)
NameError: name 'np' is not defined

arr.shape contains axis sizes (3, 2), not axis numbers. A 2D array has axes 0 and 1.

Normalizing a Vector

The normalized vector should have mean 0. Correct the formula.

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
mean = np.mean(arr)
std = np.std(arr)
arr_norm = arr - mean / std
print(np.mean(arr_norm))
NameError: name 'np' is not defined
arr_norm = (arr - mean) / std
print(np.mean(arr_norm))
NameError: name 'arr' is not defined

The subtraction must happen before division. Floating-point arithmetic may display a value extremely close to zero rather than exactly zero.

Accumulation or One Final Total?

Predict each output. Which NumPy expression reproduces the final value of the loop?

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

for value in arr:
    result = result - value

print(result)
print(1 - np.cumsum(arr))
print(1 - np.sum(arr))
print(1 - np.diff(arr))
NameError: name 'np' is not defined
  • The loop ends at -14.
  • 1 - np.cumsum(arr) returns [0, -2, -5, -9, -14].
  • 1 - np.sum(arr) returns -14, matching the loop’s final value.
  • 1 - np.diff(arr) returns [0, 0, 0, 0].

Session 18: Linear Algebra

A Matrix Times Its Inverse

Predict z. Why might some entries not display as exact integers?

x = np.array([[1, 2], [3, 4]])
y = np.linalg.inv(x)
z = x @ y
print(z)
NameError: name 'np' is not defined

Mathematically, z is the identity matrix. Floating-point calculations may produce tiny errors such as 2.22044605e-16 instead of zero. Use np.allclose(z, np.eye(2)) to test approximate equality:

print(np.allclose(z, np.eye(2)))
NameError: name 'np' is not defined

Converting directly to integers is unsafe because conversion truncates rather than rounds.

Finding Eigenvalues

Which call returns the eigenvalues and eigenvectors of a square matrix?

X = np.array([[1, 2], [3, 4]])
NameError: name 'np' is not defined

Choose among np.eigen(X), np.eigvals(X), and np.linalg.eig(X).

eigenvalues, eigenvectors = np.linalg.eig(X)
print(eigenvalues)
print(eigenvectors)
NameError: name 'np' is not defined

np.linalg.eig() returns both results. np.linalg.eigvals() returns only eigenvalues; np.eigvals() and np.eigen() do not exist.


Session 19: Vectorization

Element-Wise Comparison

Predict c.

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = a > b
NameError: name 'np' is not defined

c is [False, False, False]. NumPy performs the comparison element by element.

Broadcasting a Column and a Row

Predict the shape and values of c.

a = np.array([[1], [2], [3]])
b = np.array([4, 5, 6])
c = a > b
NameError: name 'np' is not defined

a has shape (3, 1) and b has shape (3,), which behaves like (1, 3). Broadcasting produces a (3, 3) array. Every comparison is false because each value in a is smaller than every value in b.

Broadcasting Failure

Why does this comparison raise a ValueError?

a = np.array([1, 2, 3])
b = np.array([4, 5, 6, 7])
c = a > b

The one-dimensional shapes (3,) and (4,) are incompatible because their only dimensions differ and neither equals 1.


Further Practice