NumPy·Lesson 18·10 min·0/4 exercises

unique and set operations

Distinct values, frequency tables, and the vectorised version of `in`.

np.uniquereturn_countsreturn_inversenp.isinnp.intersect1dnp.union1dnp.setdiff1d

Watch it happen

Play it through, or step back and forth yourself.

x
0
1
2
3
4
5
6
3
1
3
7
1
1
9
shape (7,)

Real data repeats itself — categories, user ids, labels. The first question is usually "which distinct values are in here, and how many of each?"

The idea

Real data repeats — categories, user ids, labels, sensor states. np.unique is the workhorse for dealing with that, and it quietly does several jobs at once.

unique sorts, too

x = np.array([3, 1, 3, 7, 1, 1, 9])
np.unique(x)     # [1 3 7 9]

The distinct values, sorted. That's not optional — it falls out of how the function works — and it's worth knowing because the output order won't match the order values first appeared in your data.

Frequency tables

values, counts = np.unique(x, return_counts=True)
# values [1 3 7 9]
# counts [3 2 1 1]

One call and you have a frequency table. This is the NumPy answer to value_counts, and pairing it with argsort from the last lesson gives you "most common first" immediately.

The other two flags

np.unique(x, return_index=True)     # where each value FIRST appeared
np.unique(x, return_inverse=True)  # how to rebuild x from the unique values

return_inverse is more useful than it looks. It gives you an integer code for every element, so uniq[inverse] reconstructs the original array exactly. That is label encoding — turning a categorical column into integers a model can eat — in one line:

cities = np.array(["Delhi", "Pune", "Delhi", "Mumbai"])
uniq, codes = np.unique(cities, return_inverse=True)
codes            # [0 2 0 1]
uniq[codes]      # back to the original

Set operations

Once duplicates are gone, the usual set questions have direct answers:

np.intersect1d(a, b)    # in both
np.union1d(a, b)        # in either
np.setdiff1d(a, b)      # in a but not b
np.setxor1d(a, b)       # in one but not both

All four return sorted, deduplicated arrays, and the 1d in the names is a real warning: they flatten their input, so they answer questions about values rather than about rows.

isin is the one you'll use most

It's the odd one out, and deliberately so. Rather than returning a set, it keeps the shape of the input and returns a boolean mask:

np.isin(x, [1, 9])       # [False True False False True True True]
x[np.isin(x, [1, 9])]    # the matching values

Because it's a mask it plugs straight into everything from lesson 7 — filtering, np.where, counting with .sum(). Think of it as the vectorised in operator, and reach for it instead of chaining a dozen | comparisons.

One performance note: np.isin sorts internally, so it stays fast even when the list of allowed values is long. A chain of == comparisons scans the array once per value, which does not.

Practice

Write it yourself. The answer is there when you want it.

Putting the kettle on…

Starting up…

Write it yourself

not graded

Call np.unique(x, return_counts=True) and print the values and counts. Rank them, most common first. Then label-encode ["Delhi", "Pune", "Delhi", "Mumbai"] with return_inverse=True, and print the labels beside their codes. Finish with np.isin(x, [1, 9]).

Write something and press Run — the output appears here.

Your turn

4 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.

Return the distinct values in x.

your answer

Return just the counts of each distinct value in x — the second half of the pair.

your answer

Return the values of x that appear in b, using np.isin.

your answer

Return the values that are in x but not in b.

your answer