Checkpoint: Array Operations

Reviewed & published by Brayan K

A checkpoint is a review lesson that consolidates a whole section — here you revisit splitting, axis, fancy indexing, NaN handling, counting, set operations, rounding, and cumulative functions in one place.

Part of the free NumPy course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You'll recap every tool, tackle a multi-step build challenge with a full worked solution, and test yourself with a short checkpoint quiz.

What You've Learned in This Section

1 One-Glance Recap Table

Every tool from this section, with the one-liner that does the job:

GoalCode
Split into 3 even piecesnp.split(arr, 3)
Per-column totalarr.sum(axis=0)
Reorder rowsarr[[2, 0, 1]]
Mean ignoring NaNnp.nanmean(arr)
Distinct values + countsnp.unique(arr, return_counts=True)
Values in both arraysnp.intersect1d(a, b)
Cap to a rangenp.clip(arr, 0, 100)
Running totalnp.cumsum(arr)

2 Warm-Up: Run the Toolkit

Before the big challenge, fire off one example of each tool to refresh your memory.

import numpy as np

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

print(np.split(np.arange(6), 3))          # [array([0, 1]), array([2, 3]), array([4, 5])]
print(np.unique(arr, return_counts=True)) # (array([1, 2, 3]), array([2, 1, 2]))
print(np.clip(arr, 0, 2))                 # [2 1 2 1 2]
print(np.cumsum(arr))                     # [ 3  4  6  7 10]
print(np.isin(arr, [1, 3]))               # [ True  True False  True  True]

# ✅ Expected output:
# [array([0, 1]), array([2, 3]), array([4, 5])]
# (array([1, 2, 3]), array([2, 1, 2]))
# [2 1 2 1 2]
# [ 3  4  6  7 10]
# [ True  True False  True  True]

🏗️ Build Challenge: Clean & Summarize a Dataset

You are handed a small 2D table of sensor readings (rows = sensors, columns = hours). It has a missing value and an outlier. Build a short pipeline that:

Try it in the editor first. The starter sets up the data and leaves the steps for you. The full solution is below if you get stuck.

import numpy as np

data = np.array([[ 20.0, 22.0, np.nan, 24.0],
                 [ 30.0, 31.0, 29.0, 999.0],
                 [ 15.0, 14.0, 16.0, 15.0]])

# STEP 1: how many readings are missing?
missing = np.isnan(data).___()   # 👉 replace ___ with sum

# STEP 2: replace NaN with the mean of the finite values
fill_value = np.nanmean(data)
clean = np.where(np.isnan(data), fill_value, data)

# STEP 3: clip into a realistic 0..100 range
clean = np.___(clean, 0, 100)   # 👉 replace ___ with clip

# STEP 4: each sensor's average reading (per row)
row_means = clean.mean(axis=___)   # 👉 replace ___ with 1

print("missing:", missing)
print(row_means)

# ✅ Expected output:
# missing: 1
# [41.5 47.5 15. ]
import numpy as np

data = np.array([[ 20.0, 22.0, np.nan, 24.0],
                 [ 30.0, 31.0, 29.0, 999.0],
                 [ 15.0, 14.0, 16.0, 15.0]])

# STEP 1: count the missing readings
missing = np.isnan(data).sum()          # 1

# STEP 2: replace NaN with the mean of the finite values
fill_value = np.nanmean(data)           # mean of all non-NaN entries
clean = np.where(np.isnan(data), fill_value, data)

# STEP 3: clip the outlier (999) and anything below 0
clean = np.clip(clean, 0, 100)

# STEP 4: per-sensor average (collapse the hours -> axis=1)
row_means = clean.mean(axis=1)

print("missing:", missing)
# missing: 1
print(np.round(row_means, 2))
# [ 41.5   47.5   15.  ]

# Note: sensor 0's NaN was filled with the global mean (110.45, which step 3 then clips to 100), and
# sensor 1's 999 outlier was clipped to 100, not removed -- both
# nudge the row averages, which is why cleaning choices matter.

! Common Errors Across This Section

❌ Reading axis=0 as "the rows"

It collapses the rows, giving a per-column result.

✅ Fix: the named axis disappears — sum(axis=0) is one total per column.

arr[arr == np.nan]   # always empty

✅ Fix: use np.isnan(arr); reduce with np.nanmean.

❌ Length mismatch after np.diff

diff returns one fewer element than its input.

✅ Fix: use prepend= or slice the original to match.

🎯 Mini Challenge: Category Report

From a list of order categories, find the distinct categories with their counts, then the most common one — using only tools from this section.

import numpy as np

orders = np.array(["food", "toys", "food", "books", "food", "toys"])

values, counts = np.unique(orders, return_counts=True)
print(values)                  # ['books' 'food' 'toys']
print(counts)                  # [1 3 2]

top = counts.argmax()
print("most common:", values[top], "(", counts[top], ")")
# most common: food ( 3 )

# ✅ Expected output:
# ['books' 'food' 'toys']
# [1 3 2]
# most common: food ( 3 )

🎯 Your turn: put the module together

The build challenge above walks you through cleaning data. This one is shorter and sharper: the same 3×3 table three times, and each blank is one of the module's core moves. The trap is the first blank — axis=0 and axis=1 both run without error and give different answers, so read the printed shape before you trust the number.

# 🎯 YOUR TURN — three ideas from this module on one small table:
# an axis reduction, fancy indexing to reorder rows, and a running total.
# Fill in the three ___ blanks.

import numpy as np

# stock on hand for 3 products across 3 days (rows = products, columns = days)
stock = np.array([[5, 7, 6],
                  [9, 9, 10],
                  [2, 4, 3]])

# 1) total per PRODUCT means collapsing the day axis (columns)
per_product = stock.sum(axis=___)
# 👉 replace ___ with 1
print("units per product:", per_product)

# 2) reorder the rows so the smallest stock comes first: rows 2, 0, 1
reordered = stock[[___]]
# 👉 replace ___ with 2, 0, 1
print("reordered rows:")
print(reordered)

# 3) running total across the days, per product
running = np.___(stock, axis=1)
# 👉 replace ___ with cumsum
print("running totals:")
print(running)

# ✅ Expected output:
# units per product: [18 28  9]
# reordered rows:
# [[ 2  4  3]
#  [ 5  7  6]
#  [ 9  9 10]]
# running totals:
# [[ 5 12 18]
#  [ 9 18 28]
#  [ 2  6  9]]

📝 Checkpoint Quiz

Answer each in your head, then expand to check.

np.array_split(arr, 3) — it allows uneven pieces, making the earlier ones one element longer. np.split would raise a ValueError.

A length-3 array — one total per row. axis=1 collapses the 4 columns, so the result has shape (3,).

By the IEEE-754 standard, NaN is defined to never equal anything, including itself. That is why you must test with np.isnan() instead of ==.

A copy. Modifying the result of arr[[0, 2]] does not change the original. Basic slicing like arr[0:2] returns a view that does share memory.

values, counts = np.unique(arr, return_counts=True). The values come back sorted, and counts lines up with them position by position.

They are inverses. cumsum builds running totals; diff recovers the step-by-step changes. Note diff returns one fewer element unless you use prepend=.

❓ Frequently Asked Questions

Checkpoint complete — array operations consolidated!

You can split and join, reason about axis, index fancily, clean NaN and outliers, count and compare sets, round and clip, and accumulate or difference — and combine them into a real pipeline.

Practice quiz

Which function splits a length-7 array into 3 parts without raising an error?

  • np.split(arr, 3)
  • np.array_split(arr, 3)
  • np.hsplit(arr, 3)
  • np.vsplit(arr, 3)

Answer: np.array_split(arr, 3). np.array_split allows uneven pieces, making the earlier ones one element longer. np.split requires the array to divide evenly and would raise a ValueError.

For a (3, 4) array, what shape does arr.sum(axis=1) return?

  • (4,)
  • (3,)
  • (3, 4)
  • a single scalar

Answer: (3,). The named axis collapses. axis=1 removes the 4 columns, leaving one total per row, so the result has shape (3,).

What does np.clip(arr, 0, 100) do to a value of 999?

  • Removes it from the array
  • Leaves it as 999
  • Caps it to 100
  • Replaces it with NaN

Answer: Caps it to 100. clip caps any value above the upper bound to that bound, so 999 becomes 100. Values below 0 would be raised to 0.

Why does the test arr[arr == np.nan] always return an empty array?

  • NaN never equals anything, even itself, so the comparison is always False
  • == is not defined for arrays
  • np.nan is actually zero
  • You must use a list instead of an array

Answer: NaN never equals anything, even itself, so the comparison is always False. By the IEEE-754 standard NaN compares unequal to everything, including itself, so == finds nothing. Use np.isnan(arr) instead.

How do you get the distinct values of an array together with how often each appears?

  • np.bincount(arr)
  • np.sort(arr)
  • np.unique(arr, return_counts=True)
  • np.intersect1d(arr, arr)

Answer: np.unique(arr, return_counts=True). np.unique(arr, return_counts=True) returns the sorted distinct values and a counts array lined up position by position.

Does fancy indexing like arr[[0, 2]] return a copy or a view?

  • A view that shares memory
  • A copy
  • It depends on the dtype
  • It raises an error

Answer: A copy. Fancy (integer-array) indexing always returns a copy. Basic slicing such as arr[0:2] returns a view that shares memory with the original.

Which call keeps only the values that appear in BOTH arrays a and b?

  • np.union1d(a, b)
  • np.setdiff1d(a, b)
  • np.intersect1d(a, b)
  • np.isin(a, b)

Answer: np.intersect1d(a, b). np.intersect1d returns the sorted, unique values common to both arrays. union1d combines them, setdiff1d subtracts, and isin returns a boolean mask.

What is the relationship between np.cumsum and np.diff?

  • They are unrelated
  • They are inverses of each other
  • Both return one fewer element than the input
  • Both require an axis argument

Answer: They are inverses of each other. cumsum builds running totals while diff recovers the step-by-step changes, so they undo each other. Note diff returns one fewer element unless you use prepend=.

Which NumPy function computes the mean while skipping NaN values?

  • np.mean
  • np.nanmean
  • np.average
  • np.nan_to_num

Answer: np.nanmean. np.nanmean ignores NaN entries when averaging. A plain np.mean would be poisoned and return NaN if any element is NaN.

After np.diff on a length-5 array, how many elements does the result have?

  • 6
  • 5
  • 4
  • 1

Answer: 4. diff returns the differences between consecutive elements, which is one fewer than the input, so a length-5 array yields a length-4 result.

Continue this course

Frequently asked questions

What does this checkpoint cover?

It reviews the whole array-operations section: splitting arrays, the axis parameter, fancy indexing, handling NaN and infinity, finding unique values and counting them, set operations, rounding and clipping, and cumulative and difference functions. The build challenge stitches several of these together into one realistic data-cleaning pipeline.

How should I use the build challenge?

Try to write it yourself in the editor first, using only what you remember. If you get stuck, expand the 'Show solution' panel for a complete, runnable version with expected output. Comparing your attempt to the solution is the fastest way to find the one concept you are still shaky on.

What is the single most important idea to retain?

The axis rule: the axis you name is the one that collapses. axis=0 gives a result per column, axis=1 gives a result per row. Almost every reduction, cumulative function, and difference function in this section accepts axis, so internalising that one mental model pays off everywhere.

What comes after this checkpoint?

The next lesson is meshgrid & Coordinate Grids, where you build 2D grids of x and y coordinates to evaluate functions over a plane — the foundation for plotting surfaces, building distance maps, and vectorising calculations that would otherwise need nested loops.