Unique Values & Counting

Reviewed & published by Brayan K

Counting is the task of finding the distinct values in an array and tallying how often each one appears, which NumPy makes a one-liner with np.unique, np.bincount, and np.count_nonzero.

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 extract sorted distinct values, get per-value counts with return_counts, histogram small integers with bincount, and count condition matches with count_nonzero.

What You'll Learn in This Lesson

1 np.unique — Sorted Distinct Values

np.unique(arr) returns the distinct values with duplicates removed, and the result is always sorted — even if the input was scrambled. It works on numbers and strings alike.

import numpy as np

arr = np.array([3, 1, 2, 1, 3, 3])
np.unique(arr)        # [1 2 3]  -> sorted, no duplicates

colors = np.array(["red", "blue", "red", "green"])
np.unique(colors)     # ['blue' 'green' 'red']
import numpy as np

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

# Sorted distinct values
print(np.unique(arr))          # [1 2 3]

# return_index -> position of the FIRST occurrence of each value
values, first_idx = np.unique(arr, return_index=True)
print(values)                  # [1 2 3]
print(first_idx)               # [1 2 0]

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

2 Counting Categories with return_counts

Add return_counts=True and np.unique hands back a second array: how many times each value occurred. Zip the two arrays together and you have an instant frequency table — the go-to way to count categories.

votes = np.array(["yes", "no", "yes", "yes", "no"])

values, counts = np.unique(votes, return_counts=True)
# values -> ['no' 'yes']
# counts -> [2 3]

dict(zip(values, counts))      # {'no': 2, 'yes': 3}
import numpy as np

votes = np.array(["yes", "no", "yes", "yes", "no"])

values, counts = np.unique(votes, return_counts=True)
print(values)                  # ['no' 'yes']
print(counts)                  # [2 3]

# Build a tidy table
for v, c in zip(values, counts):
    print(v, "->", c)
# no -> 2
# yes -> 3

# ✅ Expected output:
# ['no' 'yes']
# [2 3]
# no -> 2
# yes -> 3

3 np.bincount & np.count_nonzero

For non-negative integers, np.bincount is a fast specialist: it returns counts indexed by value, so result[k] is how many times k appeared. Separately, np.count_nonzero counts entries that are not zero — perfect for counting how many elements pass a condition.

labels = np.array([0, 1, 1, 2, 1, 0])
np.bincount(labels)        # [2 3 1]  -> two 0s, three 1s, one 2

scores = np.array([55, 80, 42, 91, 67])
np.count_nonzero(scores >= 60)   # 3  -> how many passed
import numpy as np

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

# bincount: index k holds the count of value k
counts = np.bincount(labels)
print(counts)                  # [2 3 1]
print("zeros:", counts[0])     # zeros: 2
print("ones:", counts[1])      # ones: 3

# count_nonzero counts how many pass a test
scores = np.array([55, 80, 42, 91, 67])
print(np.count_nonzero(scores >= 60))   # 3

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

🎯 YOUR TURN: Fill in the Blank

Replace ___ with the keyword argument that makes np.unique also return per-value counts.

import numpy as np

fruits = np.array(["apple", "pear", "apple", "apple", "pear"])

# 🎯 YOUR TURN:
# Get each fruit AND how many times it appears.
# Hint: ask unique to also return the counts.
values, counts = np.unique(fruits, ___=True)

print(values)                  # ['apple' 'pear']
print(counts)
# Expected output:
# [3 2]

Answer: return_counts — it returns a counts array matching the sorted values.

! Common Errors (And How to Fix Them)

❌ bincount on negative or float values

np.bincount([0, -1, 2])   # ❌ ValueError: negative not allowed

✅ Fix: bincount needs non-negative integers; use np.unique(arr, return_counts=True) for other data.

❌ Forgetting to unpack two return values

vals = np.unique(arr, return_counts=True)   # vals is a TUPLE

✅ Fix: unpack both, e.g. values, counts = np.unique(arr, return_counts=True).

❌ Expecting unique to keep original order

np.unique always sorts the output.

✅ Fix: use return_index=True and sort by that index if you need first-appearance order.

🎯 Mini Challenge: Find the Most Common Value

Given a list of survey ratings, find which rating was most common and how many times it appeared, using unique with counts and argmax.

import numpy as np

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

values, counts = np.unique(ratings, return_counts=True)
print(values)                  # [2 3 4 5]
print(counts)                  # [1 1 3 4]

# argmax finds the index of the largest count
top = counts.argmax()
print("most common:", values[top])    # most common: 5
print("appeared:", counts[top])       # appeared: 4

# ✅ Expected output:
# [2 3 4 5]
# [1 1 3 4]
# most common: 5
# appeared: 4

❓ Frequently Asked Questions

Lesson complete — you can count anything!

You can extract sorted distinct values with np.unique, tally categories with return_counts, histogram integers with np.bincount, and count matches with np.count_nonzero.

Practice quiz

What does np.unique([3, 1, 2, 1, 3, 3]) return?

  • [3 1 2]
  • [3 3 3 1 1 2]
  • [1 2 3]
  • [3 2 1]

Answer: [1 2 3]. unique returns sorted distinct values with duplicates removed.

Is the output of np.unique always sorted?

  • No, it keeps input order
  • Yes, it is always sorted ascending
  • Only for strings
  • Only for integers

Answer: Yes, it is always sorted ascending. np.unique always returns its result sorted, even if the input was not.

How do you get per-value counts from np.unique?

  • Pass return_counts=True
  • Pass sort=True
  • Use np.tally
  • Pass count=True

Answer: Pass return_counts=True. return_counts=True returns the values plus a matching counts array.

What does np.bincount([0, 1, 1, 2, 1, 0]) return?

  • [0 1 2]
  • [6]
  • [1 2 3]
  • [2 3 1]

Answer: [2 3 1]. Index k holds the count of value k: two 0s, three 1s, one 2.

What does np.count_nonzero([55,80,42,91,67] >= 60) return?

  • 5
  • 2
  • 3
  • 0

Answer: 3. Three values (80, 91, 67) pass the >= 60 test.

When counts line up with np.unique's values, what order are they in?

  • Input order
  • Sorted value order
  • Reverse order
  • Random order

Answer: Sorted value order. Counts match the sorted values, so pair counts[i] with values[i].

Which is the fast specialist for counting non-negative integers?

  • np.bincount
  • np.sort
  • np.where
  • np.repeat

Answer: np.bincount. np.bincount counts each integer from 0 up to the maximum.

What does return_index=True give you from np.unique?

  • The counts of each value
  • The sorted values reversed
  • Nothing extra
  • The position of each value's first occurrence

Answer: The position of each value's first occurrence. return_index returns the index of the first occurrence of each value.

Why does np.bincount([0, -1, 2]) fail?

  • The list is too short
  • bincount needs non-negative integers
  • It must be sorted first
  • Floats are required

Answer: bincount needs non-negative integers. bincount only works on non-negative integers; negatives raise a ValueError.

On a boolean mask, how does count_nonzero compare to sum?

  • sum is always larger
  • count_nonzero rounds the result
  • They give the same number (True counts as 1)
  • count_nonzero only works on floats

Answer: They give the same number (True counts as 1). Both count the True entries, since True is 1.

Continue this course

Frequently asked questions

What does np.unique return?

By default np.unique returns the sorted array of distinct values, with every duplicate removed. So np.unique([3, 1, 2, 1, 3]) gives [1 2 3]. Importantly the result is always sorted, even if the input was not, which makes it predictable for building categories or lookups.

How do I count how many times each value appears?

Pass return_counts=True to np.unique. It then returns two arrays: the unique values and a matching array of how many times each one occurred. Zipping them together gives you a clean value-to-count table, which is the fastest way to tally categories in NumPy.

When should I use np.bincount instead of np.unique?

np.bincount is a fast specialist for non-negative integers. It counts how many times each integer from 0 up to the maximum appears, returning a count array indexed by value. It is ideal for things like histogramming small integer labels, but it only works on non-negative ints, whereas np.unique works on any dtype.

What is the difference between count_nonzero and sum on a boolean mask?

On a boolean array both give the same number, because True counts as 1. np.count_nonzero(mask) is the more explicit, slightly faster way to count how many elements satisfy a condition, and it also works on numeric arrays to count entries that are not zero. Many people prefer it for readability when counting matches.