Q&A library Python

Where do missing values go when I sort in pandas?

Python Intermediate missing-datana-positionsorting

Short answer

Missing values are placed last by default, regardless of sort direction. Use na_position="first" to move them to the top, and remember they are never compared, only positioned.

The short version

By default, sort_values puts missing values last, whatever the sort direction. They are not treated as large or small; they are simply positioned at the end. Pass na_position="first" to put them at the top instead.

The default

import pandas as pd
import numpy as np

s = pd.Series([3, np.nan, 1])

s.sort_values().tolist()                        # [1.0, 3.0, nan]
s.sort_values(na_position="first").tolist()     # [nan, 1.0, 3.0]

Ascending or descending, the NaN still lands at the end unless you say otherwise, because it is placed rather than compared.

Why they are not compared

NaN is not equal to, greater than, or less than anything, including itself. So there is no answer to "is NaN bigger than 3", and a sort cannot place it by comparison. Pandas sidesteps the question by putting missing values at one end, and lets you choose which end.

Where this catches people out

Sorting descending to find the largest values, then taking the top rows, can pull in NaN rows if you are not careful.

s.sort_values(ascending=False).head(2)   # [3.0, 1.0] , NaN still last, fine

Here it works, but if you had used na_position="first" the top rows would be missing values, which would silently poison a "top performers" list. Be explicit about where you want them.

Sorting a frame by a column with gaps

The same rules apply to sort_values on a DataFrame, and the whole row travels with its key.

df.sort_values("amount", na_position="first")

This is a useful trick for reviewing incomplete records: sort them to the top so you can inspect them first.

Dropping them before sorting

If missing values have no place in the output at all, remove them before sorting rather than relying on their position.

df.dropna(subset=["amount"]).sort_values("amount", ascending=False)

This is clearer than sorting and then slicing around the NaN values, and it makes the decision to exclude them explicit.

rank also ignores missing values by default, leaving them as NaN in the result rather than giving them a position. That is usually right, but if you need them ranked, na_option controls whether they go top, bottom, or stay NaN. As with sorting, choose deliberately, because the default is a decision too.

The general principle

Missing values need a policy at every step: sorting, ranking, grouping, aggregating. Each has a sensible default, but the defaults differ, and none of them warn you. Knowing what each one does with NaN is a large part of getting reliable results from messy data.

Need this solved on your own data?

The library is free and always will be. When the problem is bigger than an answer, we take on the work.

Start a project