Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsiTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
To get the distinct values in a NumPy array and how often each one occurs, call np.unique(a, return_counts=True). To get the distinct rows of a 2D array, call np.unique(a, axis=0), and for distinct columns use axis=1. Add return_inverse=True when you need to rebuild the original array from the unique results.
Unique values and their counts
With the default axis=None, np.unique flattens a multidimensional input and returns its distinct scalar values in sorted order. Setting return_counts=True adds a second array whose entries line up position by position with the unique values.
import numpy as np
a = np.array([3, 1, 2, 3, 1, 3])
values, counts = np.unique(a, return_counts=True)
print(values) # [1 2 3]
print(counts) # [2 1 3]
Here the value 1 appears twice, 2 once and 3 three times. The same call works on a 2D array; the input is flattened first, so every element counts individually regardless of its row.
Unique rows and unique columns
Passing axis=0 treats each row as one item and removes duplicate rows. Passing axis=1 does the same for columns. Subarrays are compared as whole units and the results are sorted lexicographically, so the first element of each row (or column) decides the order first.
#1 Best Overall
rows = np.array([[1, 2],
[3, 4],
[1, 2]])
unique_rows, row_counts = np.unique(rows, axis=0, return_counts=True)
print(unique_rows) # [[1 2]
# [3 4]]
print(row_counts) # [2 1]
cols = np.array([[2, 1, 2],
[4, 3, 4]])
unique_cols = np.unique(cols, axis=1)
print(unique_cols) # [[1 2]
# [3 4]]
In the column example, the columns [2, 4] appear twice and [1, 3] once. The output keeps one copy of each distinct column, ordered lexicographically.
Axis-based uniqueness has one hard limit: object arrays, and structured arrays that contain objects, are not supported when axis is given. Use a numeric or string dtype for row and column deduplication, or convert the data first.
Rebuilding the original array with inverse indices
With return_inverse=True, the second returned array gives, for each input element, the position of its value in the unique array. Indexing the unique array with those positions reproduces the input.
Free tools Windows power users keep installed
One-click scans. No signup required.
a = np.array([3, 1, 2, 3, 1, 3])
unique_values, inverse = np.unique(a, return_inverse=True)
print(inverse) # [2 0 1 2 0 2]
print(unique_values[inverse]) # [3 1 2 3 1 3]
This is the only one of these outputs that preserves original order. Repeating each unique value by its count, for example with np.repeat(values, counts), gives back the same multiset of elements, but sorted, not in the order they appeared in the input.
Finding first occurrences with return_index
return_index=True returns the index in the input where each unique value first appears. This is useful when you need a representative element from each group, such as the first record for each key.
a = np.array([3, 1, 2, 3, 1, 3])
values, first_idx = np.unique(a, return_index=True)
print(values) # [1 2 3]
print(first_idx) # [1 2 0]
How NaN values are handled
The equal_nan parameter, introduced in NumPy 1.24, defaults to True in the current stable reference, so repeated NaN values are collapsed into a single NaN in the output. Set equal_nan=False only if you want each NaN kept as a separate result; NaN values do not compare equal to each other, so each one would then appear on its own.
Rank #4
x = np.array([1.0, np.nan, np.nan])
print(np.unique(x)) # [ 1. nan]
print(np.unique(x, equal_nan=False)) # [ 1. nan nan]
The equal_nan behaviour is documented in the numpy.unique reference.
Recommended Free Tools
The sorted parameter and unsorted output
The sorted parameter was added in NumPy 2.3. Its documentation says that results may still come out sorted when sorted=False is set, and that this behaviour may change in future releases. Do not write code that depends on a particular unsorted order. If your algorithm needs a specific order, sort the result yourself or use return_index to recover the original first-occurrence order.
Best Value
Inverse shape changes in NumPy 2.0
NumPy 2.0 changed the shape of the inverse output for multidimensional inputs. Code written for NumPy 1.x may therefore see a different shape from the inverse array on 2.x. The reference documents the change, and the simplest portable approach is to flatten the inverse with inverse.reshape(-1) before indexing. For axis-based reconstruction of multidimensional data, the reference shows np.take(unique, unique_inverse, axis=axis). Check the expected shape in the NumPy version your project targets before relying on it.
unique_values, inverse = np.unique(a, return_inverse=True)
inverse = inverse.reshape(-1)
reconstructed = unique_values[inverse].reshape(a.shape)
Choosing the right call
| Goal | Call | What you get back |
|---|---|---|
| Distinct scalar values from any array | np.unique(a) |
Sorted 1D array of flattened unique values |
| Frequency of each distinct value | np.unique(a, return_counts=True) |
Values plus counts aligned by position |
| Distinct rows of a 2D array | np.unique(a, axis=0) |
Unique rows, sorted lexicographically |
| Distinct columns of a 2D array | np.unique(a, axis=1) |
Unique columns, sorted lexicographically |
| Rebuild the original arrangement | np.unique(a, return_inverse=True) |
Unique values plus inverse indices |
| A representative element per group | np.unique(a, return_index=True) |
Unique values plus first-occurrence indices |
Decide first what counts as one item: a scalar after flattening, a row, or a column. Then choose the extra outputs your task needs. Counts answer frequency questions, first-occurrence indices locate representatives, and inverse indices restore the original layout.
Quick Recap
“
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

