Sorting, Searching & Counting

Reviewed & published by Brayan K

Sorting, searching, and counting are the tools for putting array data in order and finding out what's inside it — NumPy provides fast, vectorized functions like np.sort, np.argsort, np.searchsorted, and np.unique for exactly this.

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.

In this lesson you'll order arrays, learn copy-vs-in-place sorting, get sort indices, search sorted data, and count values and non-zeros.

What You'll Learn in This Lesson

1 np.sort (copy) vs arr.sort() (in place)

np.sort(arr) returns a new sorted copy and leaves the original untouched. The method arr.sort() sorts in place — it changes the original array and returns None. Choosing the right one avoids surprising bugs.

import numpy as np

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

# np.sort returns a new copy; original is unchanged
copy_sorted = np.sort(original)
print(copy_sorted)    # [1 2 3]
print(original)       # [3 1 2]  -> still original order

# arr.sort() sorts in place and returns None
original.sort()
print(original)       # [1 2 3]  -> now changed

# ✅ Expected output:
# [1 2 3]
# [3 1 2]
# [1 2 3]

Expected output: [1 2 3], then the original still [3 1 2], and finally [1 2 3] after the in-place sort.

2 Sort Indices: np.argsort

np.argsort(arr) returns the indices that would sort the array. Indexing the array with those indices gives the sorted values — and the same indices can reorder a parallel array, which is how you sort one list by another (e.g. sort names by their scores).

import numpy as np

scores = np.array([50, 90, 70])
names = np.array(["Ann", "Bob", "Cara"])

order = np.argsort(scores)
print(order)              # [0 2 1]  -> positions of smallest to largest

print(scores[order])      # [50 70 90]
print(names[order])       # ['Ann' 'Cara' 'Bob']  -> names sorted by score

# ✅ Expected output:
# [0 2 1]
# [50 70 90]
# ['Ann' 'Cara' 'Bob']

Expected output: [0 2 1], then [50 70 90], then ['Ann' 'Cara' 'Bob'].

3 Sorting a 2D Array Along an Axis

For 2D arrays, the axis argument controls the direction. axis=1 sorts each row left to right; axis=0 sorts each column top to bottom. The default is the last axis.

import numpy as np

grid = np.array([[3, 1, 2],
                 [9, 7, 8]])

print(np.sort(grid, axis=1))   # sort each row (left to right)
# [[1 2 3]
#  [7 8 9]]
print(np.sort(grid, axis=0))   # sort each column (top to bottom)
# [[3 1 2]
#  [9 7 8]]

# ✅ Expected output:
# [[1 2 3]
#  [7 8 9]]
# [[3 1 2]
#  [9 7 8]]

Expected output: rows sorted to [[1 2 3] [7 8 9]], and columns already in order so axis=0 leaves [[3 1 2] [9 7 8]] unchanged.

4 Searching (searchsorted) & Counting (unique, count_nonzero)

np.searchsorted(sorted_arr, value) uses binary search to find the index where value would be inserted to keep the array sorted. For counting, np.unique(arr, return_counts=True) returns the sorted unique values and how often each appears, np.count_nonzero counts non-zero (or True) elements, and np.argmax / np.argmin return the index of the largest / smallest value.

import numpy as np

sorted_arr = np.array([10, 20, 30, 40, 50])

# Where would 25 go to keep things sorted?
print(np.searchsorted(sorted_arr, 25))    # 2
# You can search for many values at once:
print(np.searchsorted(sorted_arr, [5, 35, 60]))   # [0 3 5]

# ✅ Expected output:
# 2
# [0 3 5]

Expected output: 2 (25 fits between 20 and 30), then [0 3 5] for the three queries.

import numpy as np

votes = np.array(["a", "b", "a", "c", "a", "b"])

values, counts = np.unique(votes, return_counts=True)
print(values)     # ['a' 'b' 'c']
print(counts)     # [3 2 1]

nums = np.array([0, 5, 0, 8, 3])
print(np.count_nonzero(nums))        # 3   -> three non-zero values
print(np.count_nonzero(nums > 4))    # 2   -> two values greater than 4
print(np.argmax(nums))               # 3   -> index of the max (8)
print(np.argmin(nums))               # 0   -> index of the min (0)

# ✅ Expected output:
# ['a' 'b' 'c']
# [3 2 1]
# 3
# 2
# 3
# 0

Expected output: values ['a' 'b' 'c'] with counts [3 2 1], then 3, 2, 3, 0.

🎯 YOUR TURN: Fill in the Blanks

Replace each ___ so the program prints a sorted copy and the unique values with their counts.

import numpy as np

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

print(np.___(data))                     # fill in: sorted copy -> [1 1 2 2 2 3]

values, counts = np.unique(data, return_counts=___)   # fill in: True
print(values)                           # [1 2 3]
print(counts)                           # [2 3 1]

Expected output: [1 1 2 2 2 3], then [1 2 3] and [2 3 1]. (Answers: sort, True.)

⚠️ Common Errors & Quick Reference

❌ arr = arr.sort() gives None

arr.sort() sorts in place and returns None, so assigning it wipes out your array.

✅ Fix: call arr.sort() alone, or use arr = np.sort(arr) for a copy.

❌ Confusing argmax with max

np.argmax returns the index of the largest value, not the value itself.

✅ Fix: use arr.max() for the value, np.argmax(arr) for its position.

TaskCode
Sorted copynp.sort(arr)
Sort in placearr.sort()
Sort indicesnp.argsort(arr)
Insertion pointnp.searchsorted(arr, v)
Unique + countsnp.unique(arr, return_counts=True)
Count non-zerosnp.count_nonzero(arr)

🏆 Mini Challenge: Top 3 Scores

Given test scores, find the three highest and the index of the very best, using sorting and argmax.

import numpy as np

scores = np.array([55, 92, 78, 88, 64, 99])

# Sort ascending, take the last 3, reverse for top-down
top3 = np.sort(scores)[-3:][::-1]
print(top3)               # [99 92 88]
print(np.argmax(scores))  # 5  -> index of the single best score
print(np.count_nonzero(scores >= 70))   # 4  -> passing scores (>= 70)

# ✅ Expected output:
# [99 92 88]
# 5
# 4

Expected output: [99 92 88], then 5, then 4 passing scores.

❓ Frequently Asked Questions

Lesson complete — you can order and analyze data!

You learned copy-vs-in-place sorting, np.argsort, axis sorting, np.searchsorted, and the counting tools np.unique, np.count_nonzero, and argmax/argmin.

Practice quiz

What does np.sort(arr) do?

  • Returns a new sorted copy, leaving the original unchanged
  • Sorts the original in place
  • Returns None
  • Reverses the array

Answer: Returns a new sorted copy, leaving the original unchanged. np.sort returns a brand-new sorted copy and leaves the original untouched.

What does the method arr.sort() return?

  • A sorted copy
  • None (it sorts in place)
  • The indices
  • The original order

Answer: None (it sorts in place). arr.sort() sorts in place and returns None, so arr = arr.sort() destroys your data.

What does np.argsort(arr) return?

  • The sorted values
  • The maximum
  • The indices that would sort the array
  • The counts

Answer: The indices that would sort the array. argsort returns the indices that would sort the array, useful for sorting a parallel array.

Which axis sorts each row of a 2D array left to right?

  • axis=0
  • axis=None
  • axis=2
  • axis=1

Answer: axis=1. axis=1 sorts along the columns within each row (left to right).

What does np.searchsorted do?

  • Finds the insertion index to keep a sorted array sorted
  • Sorts in reverse
  • Counts elements
  • Removes duplicates

Answer: Finds the insertion index to keep a sorted array sorted. searchsorted uses binary search to find where a value would be inserted to stay sorted.

How do you get unique values with their counts?

  • np.sort(arr)
  • np.unique(arr, return_counts=True)
  • np.argsort(arr)
  • np.count_nonzero(arr)

Answer: np.unique(arr, return_counts=True). np.unique with return_counts=True returns sorted unique values and their counts.

What does np.argmax(arr) return?

  • The maximum value
  • The sum
  • The index of the largest value
  • The sorted array

Answer: The index of the largest value. argmax returns the index of the largest value, not the value itself.

What does np.count_nonzero(arr > 4) count?

  • The total elements
  • The maximum
  • The first index
  • How many elements are greater than 4

Answer: How many elements are greater than 4. Applied to a boolean mask, count_nonzero counts the True elements, i.e. those greater than 4.

Why prefer np.sort(arr) when you must keep the original order?

  • It returns a copy and leaves the original intact
  • It is faster
  • It sorts in reverse
  • It removes duplicates

Answer: It returns a copy and leaves the original intact. np.sort produces a new array, so the original ordering is preserved.

What does arr.max() give compared to np.argmax(arr)?

  • The index, while argmax gives the value
  • Nothing different
  • The value, while argmax gives its index
  • Both give the index

Answer: The value, while argmax gives its index. arr.max() returns the value; np.argmax returns its position.

Continue this course

Frequently asked questions

What is the difference between np.sort and arr.sort()?

np.sort(arr) returns a brand-new sorted copy and leaves the original array untouched. The method arr.sort() sorts the array in place and returns None, modifying the original. Use np.sort when you need to keep the original order.

What does np.argsort return?

np.argsort returns the indices that would sort the array, not the sorted values themselves. Indexing the original array with those indices gives you the sorted data, and the same indices can reorder a parallel array — handy for sorting one array by another.

How do I count how many times each value appears?

Use np.unique(arr, return_counts=True). It returns two arrays: the sorted unique values and how many times each one occurs. It is the standard way to build a frequency table in NumPy.

What is np.searchsorted used for?

np.searchsorted finds the index where a value could be inserted into a sorted array to keep it sorted. It uses binary search, so it is very fast, and it is great for bucketing or looking things up in sorted data.