Q&A library Python

What do the different rank methods mean in pandas?

Python Advanced rankrankingties

Short answer

The method argument decides how ties are ranked. min and dense both give tied rows the same rank but differ on the gap after them, first breaks ties by order, and average splits the rank.

The short version

rank assigns positions, and its method argument controls what happens on ties. min gives tied rows the lowest shared rank and leaves a gap after them; dense shares the rank but leaves no gap; first breaks ties by row order so every rank is unique; average (the default) gives tied rows the mean of the positions they span. Choosing the wrong one quietly changes your results.

Seeing all four

import pandas as pd

s = pd.Series([10, 20, 20, 40])

print("min    ", s.rank(method="min").tolist())
print("dense  ", s.rank(method="dense").tolist())
print("first  ", s.rank(method="first").tolist())
print("average", s.rank(method="average").tolist())
min     [1.0, 2.0, 2.0, 4.0]
dense   [1.0, 2.0, 2.0, 3.0]
first   [1.0, 2.0, 3.0, 4.0]
average [1.0, 2.5, 2.5, 4.0]

The two tied 20s are the key. min ranks both 2 and jumps to 4. dense ranks both 2 and continues at 3, with no gap. first gives them 2 and 3 in the order they appear. average gives both 2.5, the mean of positions 2 and 3.

Matching the method to the meaning

These correspond to the familiar SQL window functions. min is RANK, dense is DENSE_RANK, and first is ROW_NUMBER. So if you want "no two rows share a position", use first. If you want "tied competitors share a place and the next place skips", use min, as in sporting results. If you want consecutive tiers with no gaps, use dense.

The default can surprise you

The default is average, which produces fractional ranks like 2.5. That is correct statistically but unexpected if you wanted whole-number positions. If you assumed integer ranks and got halves, you have hit the default; pick min, dense or first explicitly.

Direction and grouping

By default rank goes from smallest to largest. Pass ascending=False to rank the largest as 1, which is usual for scores or amounts. To rank within groups rather than the whole column, combine with groupby.

df["rank"] = df.groupby("city")["amount"].rank(method="dense", ascending=False)

This gives each row its position within its own city, which is the building block for per-group top-N logic.

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