Where do missing values go when I sort in pandas?
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.
The related rank behaviour
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.