Skip to main content
v2026.11,858 entries · CC-BY 4.0

Numpy Unique: Syntax, Counts, Unique Rows and Order

How numpy unique works: counts, unique rows, NaN handling across versions, and keeping first-appearance order, from the NumPy manual.

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_inverse gives, 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

  1. Import NumPy: import numpy as np
  2. Pass your array: values = np.unique(a)
  3. To count how many distinct values there are, take the size of the result: np.unique(a).size, or len(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 times values[k] occurs.
  • NaN handling. The equal_nan parameter, added in NumPy 1.24, defaults to True, so multiple NaN values collapse into one in the output. Since NumPy 1.21 a NaN is sorted to the end. Passing equal_nan=False keeps 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=False gives 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 when axis was not None, which was later reverted. The manual says inverse.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_index recipe in step 6, or pandas.unique if the data is already in a pandas object.
  • NaN appears once even though the data has several. That is the default equal_nan=True. Set equal_nan=False to keep each NaN separate. Code written for NumPy before 1.24 may rely on the older behaviour, so check your installed version with np.__version__.
  • An error when using axis on an object array. The manual says object arrays, and structured arrays containing objects, are not supported with axis.
  • 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, True equals 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)

Related CASRAI guides: numpy random choice, descriptive statistics, levels of measurement.

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about Numpy Unique: Syntax, Counts, Unique Rows and Order

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →

Ask CASRAI · Regulatory Radar

Research-admin question? Get an answer that links its sources.

An AI assistant specialized in research administration. Every answer links its sources to check before you act. 2 questions free, no account. $29/month after.

  • Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.
  • Every answer numbers its sources and links each one, so you can check the source yourself.