Written and maintained by CASRAI Editorial Board
Last updated
numpy unique, written np.unique, takes an array and returns its distinct values in sorted order. Add return_counts=True to get a frequency table, axis=0 to get unique rows, or return_index and return_inverse to map between the original array and the unique values. This guide covers each option, how NaN is handled across NumPy versions, and how to keep the order in which values first appeared, which np.unique does not do by default.
What it is used for in research
In a research workflow np.unique answers four routine questions about a column or array of data:
- Which distinct values occur? For example, the set of group labels or site codes actually present in a dataset.
- How many times does each occur? This is a frequency table; see relative frequency and frequency distributions for how to report one, and how to make a frequency table for the general method.
- Which rows are duplicates? With
axis=0, identical rows collapse into one. - How do labels map to integer codes?
return_inversegives, for every original element, its position in the unique array.
The NumPy manual states the signature as numpy.unique(ar, return_index=False, return_inverse=False, return_counts=False, axis=None, *, equal_nan=True, sorted=True). The return value is the sorted unique elements, plus up to three optional arrays depending on the flags you set. If ar is not one-dimensional it is flattened unless axis is given.
The basic call from the manual:
import numpy as np
np.unique([1, 1, 2, 2, 3, 3])
# array([1, 2, 3])
a = np.array([[1, 1], [2, 3]])
np.unique(a)
# array([1, 2, 3])
Install or access
NumPy’s official install page gives these routes. With pip: pip install numpy. With conda: conda create -n my-env, then conda activate my-env, then conda install numpy. To keep a project isolated with a virtual environment: python -m venv my-env, then source my-env/bin/activate on macOS or Linux, or my-env\Scripts\activate on Windows, then install with pip. To check the install, run import numpy as np; print(np.__version__). For sharing an environment with collaborators, see sharing a reproducible conda environment.
Step-by-step task
1. Get the unique values
- Import NumPy:
import numpy as np - Pass your array:
values = np.unique(a) - To count how many distinct values there are, take the size of the result:
np.unique(a).size, orlen(np.unique(a))for a one-dimensional result.
2. Count how often each value occurs (return_counts)
Set return_counts=True and the function returns a second array with the number of times each unique value appears. Using the example array from the NumPy manual:
a = np.array([1, 2, 6, 4, 2, 3, 2])
values, counts = np.unique(a, return_counts=True)
# values: array([1, 2, 3, 4, 6])
# counts: array([1, 3, 1, 1, 1])
The value 2 appears three times, and every other value once. The manual notes that np.repeat(values, counts) rebuilds the data as array([1, 2, 2, 2, 3, 4, 6]), which is sorted: the original order is not preserved. To print a two-column table for a string array, pair the arrays: dict(zip(values.tolist(), counts.tolist())).
3. Find where each value first appeared (return_index)
With return_index=True the function returns the indices of the first occurrences of the unique values in the original array. The manual’s example:
a = np.array(['a', 'b', 'b', 'c', 'a'])
u, indices = np.unique(a, return_index=True)
# u: array(['a', 'b', 'c'])
# indices: array([0, 1, 3])
Indexing the original with these indices, a[indices], gives the unique values again.
4. Map every element to its unique value (return_inverse)
With return_inverse=True the function returns, for each element of the input, its index in the unique array. This is the standard way to turn labels into integer codes:
a = np.array([1, 2, 6, 4, 2, 3, 2])
u, inverse = np.unique(a, return_inverse=True)
# u: array([1, 2, 3, 4, 6])
# inverse: array([0, 1, 4, 3, 1, 2, 1])
u[inverse] # rebuilds a
You can request several outputs at once. The arrays come back in a fixed order: the unique values, then the first-occurrence indices, then the inverse, then the counts, with only the ones you asked for included.
5. Find unique rows (axis=0)
With axis set to an integer, the subarrays along that axis are treated as the elements. For unique rows of a 2-D array use axis=0:
a = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])
np.unique(a, axis=0)
# array([[1, 0, 0],
# [2, 3, 4]])
The manual states that object arrays and structured arrays that contain objects are not supported when axis is given.
6. Keep first-appearance order
np.unique returns sorted values, so the order in which values first appeared in your data is lost. The documented return_index output gives a way to recover it: take the first-occurrence indices, sort them, and index the original array with the sorted indices.
x = np.array([3, 1, 3, 2, 1])
u, idx = np.unique(x, return_index=True)
x[np.sort(idx)]
# array([3, 1, 2])
If your data is in pandas, pandas.unique is built for this. Its documentation says uniques are returned in order of appearance and that it does not sort: pd.unique(pd.Series([2, 1, 3, 3])) returns array([2, 1, 3]). Related to this, the sorted keyword added in NumPy 2.3 defaults to True, and the manual warns that elements may be sorted in practice even if sorted=False, but that this could change without notice. Do not rely on sorted=False for any ordering.
Output and how to read it
- Unique values are always the first output, sorted ascending. Counts and indices line up with this array position by position:
counts[k]is the number of timesvalues[k]occurs. - NaN handling. The
equal_nanparameter, added in NumPy 1.24, defaults toTrue, so multiple NaN values collapse into one in the output. Since NumPy 1.21 a NaN is sorted to the end. Passingequal_nan=Falsekeeps each NaN as its own element. With floats[1.0, nan, nan, 0.5]the default gives one NaN at the end (0.5, 1.0, nan);equal_nan=Falsegives two. - The inverse array and NumPy 2.0. The manual records that in NumPy 2.0 the inverse for multi-dimensional input is reshaped so that
np.take(unique, unique_inverse, axis=axis)reconstructs the input. NumPy 2.0.0 also returned a higher-dimensional inverse whenaxiswas not None, which was later reverted. The manual saysinverse.reshape(-1)works on both behaviours, so use it if your code must run on several NumPy versions.
Troubleshooting
- The result is sorted but you wanted the original order. Use the
return_indexrecipe in step 6, orpandas.uniqueif the data is already in a pandas object. - NaN appears once even though the data has several. That is the default
equal_nan=True. Setequal_nan=Falseto keep each NaN separate. Code written for NumPy before 1.24 may rely on the older behaviour, so check your installed version withnp.__version__. - An error when using
axison an object array. The manual says object arrays, and structured arrays containing objects, are not supported withaxis. - The inverse array has a different shape on a different NumPy version. Use
inverse.reshape(-1)as the manual recommends. - Counts do not sum to what you expect after missing values. With the default settings all NaN values are collapsed into a single entry, so the counts reflect that one entry. For choosing how to treat missing values, see MCAR, MAR and MNAR: diagnosing your missing data.
- Booleans and integers in object-dtype pandas data. The pandas documentation notes that for object dtype,
Trueequals 1, so booleans and integers may not be distinguished.
Free alternatives
Which one fits depends on whether you need sorted output or first-appearance order, and whether your data is in an array or a pandas object.
| Tool | Order of output | Notes from the official documentation |
|---|---|---|
numpy.unique |
Sorted | Optional first-occurrence indices, inverse and counts; unique rows with axis; equal_nan controls NaN. |
pandas.unique |
Order of appearance, not sorted | Returns an Index, ExtensionArray or ndarray depending on the input type. |
FAQ
How do I count the unique values in a NumPy array?
Take the size of the result: np.unique(a).size. This counts distinct values only. To count how often each value occurs, add return_counts=True and read the second array.
Does np.unique keep the original order?
No. It returns the sorted unique values. To keep the order of first appearance, use return_index=True, sort the returned indices, and index the original array, or use pandas.unique.
How do I get unique rows of a 2-D array?
Call np.unique(a, axis=0). Identical rows are returned once, in sorted order. Object arrays are not supported with axis.
Does np.unique treat NaN values as equal?
By default yes. equal_nan=True (added in NumPy 1.24) collapses multiple NaN into one, sorted to the end. Set equal_nan=False to keep them separate.
What does return_inverse give me?
For each element of the input, the index of its value in the unique array. Indexing the unique array with it, u[inverse], rebuilds the original data.
Sources (official docs)
- NumPy Developers, numpy.unique, NumPy manual (v2.5), checked 2026-10-29.
- pandas development team, pandas.unique, pandas documentation, checked 2026-10-29.
- NumPy, Installing NumPy, checked 2026-10-29.
Related CASRAI guides: numpy random choice, descriptive statistics, levels of measurement.








