Ranking & Top-N (rank, nlargest)

Reviewed & published by Brayan K

Ranking assigns each value its position in the ordering — 1st, 2nd, 3rd — while top-N helpers like nlargest pull out just the highest or lowest rows without sorting everything.

Part of the free Pandas course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You'll learn Series.rank() with its tie methods, nlargest/nsmallest, ranking within groups, and percentile ranks with pct=True.

What You'll Learn in This Lesson

1 rank() and the Tie-Breaking Methods

Series.rank() replaces each value with its ordinal position, smallest first by default. The interesting part is ties, controlled by method=:

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

s.rank(method="min")    # 1, 2, 2, 4   (gap after the tie)
s.rank(method="dense")  # 1, 2, 2, 3   (no gap)
s.rank(method="first")  # 1, 2, 3, 4   (ties keep input order)
import pandas as pd

s = pd.Series([10, 20, 20, 30], index=["a", "b", "c", "d"])

# "min" leaves a gap after the tie -> d is 4
print(s.rank(method="min"))

# "dense" leaves NO gap -> d is 3
print(s.rank(method="dense"))

# ✅ Expected output:
# a    1.0
# b    2.0
# c    2.0
# d    4.0
# dtype: float64
# a    1.0
# b    2.0
# c    2.0
# d    3.0
# dtype: float64

2 Top-N Rows with nlargest and nsmallest

When you only want the extremes, df.nlargest(n, "col") returns the n rows with the biggest values in that column, already sorted, and nsmallest does the same for the lowest. It is shorter and faster than fully sorting because pandas only has to locate n items.

df.nlargest(3, "sales")    # top 3 rows by sales
df.nsmallest(2, "sales")   # bottom 2 rows by sales

# equivalent but does more work:
df.sort_values("sales", ascending=False).head(3)
import pandas as pd

df = pd.DataFrame({
    "product": ["A", "B", "C", "D", "E"],
    "sales":   [120, 340, 90, 340, 210],
})

# Top 3 products by sales (already sorted)
print(df.nlargest(3, "sales"))

# Bottom 2 products by sales
print(df.nsmallest(2, "sales"))

# ✅ Expected output:
#   product  sales
# 1       B    340
# 3       D    340
# 4       E    210
#   product  sales
# 2       C     90
# 0       A    120

3 Ranking Within Groups and Percentiles

Combine groupby with rank to rank inside each group — every team or department restarts at 1. And pct=True turns the rank into a percentile between 0 and 1, useful for comparing standing on a common scale.

# Rank each player within their team, best first
df["team_rank"] = (
    df.groupby("team")["points"].rank(method="dense", ascending=False)
)

# Percentile rank (0..1) instead of position
s.rank(pct=True)
import pandas as pd

df = pd.DataFrame({
    "team":   ["X", "X", "X", "Y", "Y"],
    "player": ["a", "b", "c", "d", "e"],
    "points": [10, 30, 20, 50, 40],
})

# Rank within each team, highest points = rank 1
df["team_rank"] = df.groupby("team")["points"].rank(
    method="dense", ascending=False)
print(df)

# Percentile rank of a simple series
print(pd.Series([10, 20, 30, 40]).rank(pct=True))

# ✅ Expected output:
#   team player  points  team_rank
# 0    X      a      10        3.0
# 1    X      b      30        1.0
# 2    X      c      20        2.0
# 3    Y      d      50        1.0
# 4    Y      e      40        2.0
# 0    0.25
# 1    0.50
# 2    0.75
# 3    1.00
# dtype: float64
import pandas as pd

scores = pd.Series([88, 95, 72, 95, 81],
                   index=["Ana", "Ben", "Cal", "Dee", "Eli"])

# 🎯 YOUR TURN:
# Build a leaderboard where the HIGHEST score is rank 1, tied scores
# share a rank, and there is NO gap afterwards (dense ranking).
# Fill in the method name and the keyword that flips the direction.

print(scores.rank(method="____", ascending=____))

# Expected output:
# Ana    2.0
# Ben    1.0
# Cal    4.0
# Dee    1.0
# Eli    3.0
# dtype: float64

! Common Errors (And How to Fix Them)

❌ Rank 1 is the smallest, not the biggest

By default rank counts up from the lowest value:

s.rank()   # ❌ the largest value gets the HIGHEST rank number

✅ Fix: flip the direction for a leaderboard:

s.rank(ascending=False)   # ✅ biggest value = rank 1

❌ Forgetting the column in nlargest

df.nlargest(3)              # ❌ TypeError: needs a column name

✅ Fix: tell it which column to rank by:

df.nlargest(3, "sales")    # ✅

🎯 Mini Challenge: Top 2 Per Department

Find the two highest earners in each department.

import pandas as pd

df = pd.DataFrame({
    "dept":    ["Sales", "Sales", "Sales", "Eng", "Eng", "Eng"],
    "emp":     ["a", "b", "c", "d", "e", "f"],
    "revenue": [100, 250, 175, 300, 120, 300],
})

# Rank within each dept (use "first" so ties get distinct ranks)
df["r"] = df.groupby("dept")["revenue"].rank(
    method="first", ascending=False)

top2 = df[df["r"] <= 2].sort_values(["dept", "r"])
print(top2)

# ✅ Expected output:
#     dept emp  revenue    r
# 3    Eng   d      300  1.0
# 5    Eng   f      300  2.0
# 1  Sales   b      250  1.0
# 2  Sales   c      175  2.0

❓ Frequently Asked Questions

Lesson complete — you can find the winners every time!

You can rank with the right tie method, flip direction for leaderboards, grab extremes with nlargest/nsmallest, rank within groups, and read percentiles with pct=True.

Practice quiz

Which Series method assigns ranks to values?

  • s.order()
  • s.rank()
  • s.sort()
  • s.position()

Answer: s.rank(). Series.rank() returns the rank of each value.

What is the DEFAULT method= used by rank()?

  • 'average'
  • 'min'
  • 'first'
  • 'dense'

Answer: 'average'. By default rank() uses method='average', averaging ranks of ties.

For [10, 20, 20, 40] with method='min', what is the rank of the two 20s?

  • 3 each
  • 2.5 each
  • 2 each
  • 1 each

Answer: 2 each. method='min' gives tied values the lowest rank in the group, so both 20s get 2.

With method='dense' on [10, 20, 20, 40], what rank does 40 get?

  • 3
  • 4
  • 2
  • 5

Answer: 3. dense leaves no gaps after ties, so 40 is rank 3.

Which method= breaks ties by order of appearance?

  • 'average'
  • 'dense'
  • 'max'
  • 'first'

Answer: 'first'. method='first' assigns ranks in the order the values appear.

How do you rank from highest value to lowest?

  • rank(ascending=False)
  • rank(reverse=True)
  • rank(top=True)
  • rank(desc=1)

Answer: rank(ascending=False). Pass ascending=False so the largest value gets rank 1.

For [10, 20, 20, 40] with method='max', what rank do the 20s get?

  • 2 each
  • 4 each
  • 3 each
  • 2.5 each

Answer: 3 each. method='max' gives tied values the highest rank in the group, so both 20s get 3.

What does rank(pct=True) return?

  • Ranks as integers
  • The count of ties
  • Sorted values
  • Ranks as percentiles between 0 and 1

Answer: Ranks as percentiles between 0 and 1. pct=True expresses each rank as a percentile of the data.

Does rank() change the order of the rows?

  • No, it returns ranks aligned to the original index
  • Yes, it sorts ascending
  • Yes, it sorts descending
  • It drops ties first

Answer: No, it returns ranks aligned to the original index. rank() keeps the original order; only the values become ranks.

Which method gives [1, 2.5, 2.5, 4] for [10, 20, 20, 40]?

  • method='min'
  • method='average'
  • method='dense'
  • method='first'

Answer: method='average'. The default 'average' averages the tied positions 2 and 3 to 2.5.

Continue this course

Frequently asked questions

What is the difference between rank(method='min') and rank(method='dense')?

Both give tied values the same rank, but they differ in what comes next. 'min' leaves a gap: two values tied for 2nd are both 2, and the next value jumps to 4. 'dense' leaves no gap: the tied values are 2 and the next value is 3. Use 'min' for competition-style ranking and 'dense' when you want consecutive rank numbers.

When should I use nlargest instead of sort_values().head()?

nlargest(n, 'col') is both shorter and faster for pulling the top n rows, because it only needs to find n items rather than fully sorting the whole DataFrame. sort_values('col', ascending=False).head(n) produces the same rows but does more work. For just the extremes, prefer nlargest / nsmallest.

How do I rank within groups instead of across the whole column?

Chain rank() after a groupby: df.groupby('team')['points'].rank(method='dense', ascending=False). Pandas restarts the ranking inside each group, so every team gets its own 1, 2, 3 ordering rather than one global ranking.

What does pct=True do?

pct=True returns each rank as a percentile between 0 and 1 instead of an integer position. The largest value (with default ascending order) gets 1.0 and the rest scale below it, which is handy for comparing relative standing across columns of different lengths.