Mastering Rank Function in Python Pandas: A Complete Guide

PYTHON Published Updated 3 mins read By Leon Wei
Mastering Rank Function in Python Pandas: A Complete Guide cover image

Quick summary

Summarize this blog with AI

rank() assigns a rank to each value while preserving the original row order. It does not sort the DataFrame. The result depends on the tie method, ascending direction, missing-value policy, and whether ranks are global or computed separately within groups.

Tested with Python 3.12.14 and pandas 2.2.3 with Copy-on-Write enabled and pandas 3.0.6. Run the blocks in order; the sales data is fictional.

Use one fixture with ties and a missing value

import pandas as pd

sales = pd.DataFrame({
    'seller': ['Ada', 'Ben', 'Cara', 'Dee', 'Eli'],
    'region': ['East', 'East', 'West', 'West', 'East'],
    'amount': [100.0, 200.0, 200.0, None, 50.0],
})
for method in ('average', 'min', 'max', 'dense', 'first'):
    ranks = sales['amount'].rank(method=method, ascending=False)
    print(method, ranks.fillna('missing').tolist())

Expected output:

average [3.0, 1.5, 1.5, 'missing', 4.0]
min [3.0, 1.0, 1.0, 'missing', 4.0]
max [3.0, 2.0, 2.0, 'missing', 4.0]
dense [2.0, 1.0, 1.0, 'missing', 3.0]
first [3.0, 1.0, 2.0, 'missing', 4.0]

Descending rank gives the largest sale the best rank. Average splits the occupied rank positions between tied values; min and max choose an endpoint. Dense advances one rank per distinct observed value. First breaks ties in current input order, so changing the input order can change which tied row wins.

Rank and sorting answer different questions

sales['rank'] = sales['amount'].rank(method='min', ascending=False)
print(sales['seller'].tolist())
ordered = sales.sort_values(['rank', 'seller'], na_position='last')
print(ordered['seller'].tolist())

Expected output:

['Ada', 'Ben', 'Cara', 'Dee', 'Eli']
['Ben', 'Cara', 'Ada', 'Eli', 'Dee']

The first output preserves the incoming order. The second explicitly sorts the calculated ranks, then uses seller name as a deterministic secondary key. This is preferable to treating accidental incoming order as a meaningful tiebreaker.

Choose how missing values participate

The default na_option='keep' leaves missing values unranked. It does not place them one rank below every observed value. Top assigns them the lowest rank numbers; bottom assigns them the highest rank numbers. These policies matter even when ranking in descending order.

for policy in ('keep', 'top', 'bottom'):
    ranks = sales['amount'].rank(
        method='min', ascending=False, na_option=policy
    )
    print(policy, ranks.fillna('missing').tolist())
print(sales['amount'].rank(pct=True).fillna('missing').tolist())

Expected output:

keep [3.0, 1.0, 1.0, 'missing', 4.0]
top [4.0, 2.0, 2.0, 1.0, 5.0]
bottom [3.0, 1.0, 1.0, 5.0, 4.0]
[0.5, 0.875, 0.875, 'missing', 0.25]

The percentile example uses ascending average ranks divided by the four ranked observations. Ties therefore produce 3.5/4 = 0.875. This is a specific rank convention, not a claim about a population percentile or a quantile interpolation rule.

Rank within each region

sales['region_rank'] = sales.groupby('region')['amount'].rank(
    method='dense', ascending=False
)
print(sales['region_rank'].fillna('missing').tolist())
print(len(sales))

Expected output:

[2.0, 1.0, 1.0, 'missing', 3.0]
5

Ben ranks first in East and Cara ranks first in West. Each region starts again at rank 1. Missing amounts stay missing. This fixture has no missing region keys; decide whether to reject such records or include a missing-key group with dropna=False.

An ordered category map is not an observed rank

priority_order = {'high': 1, 'medium': 2, 'low': 3}
priorities = pd.Series(['high', 'low', 'medium', 'unknown'])
codes = priorities.map(priority_order)
print(codes.fillna('unrecognized').tolist())

Expected output:

[1.0, 3.0, 2.0, 'unrecognized']

This map encodes a business-defined order independently of which categories occur in the current sample. Unknown categories must be handled explicitly. Alphabetical order would put these words in a different sequence and would not express the business rule.

Keep the comparison set intact

Select intended columns explicitly. Current DataFrame.rank accepts numeric_only=False by default; this guide does not rely on the former numeric_only=None signature. Ranking each file chunk independently and concatenating the results does not produce global ranks because every chunk starts its own comparison set. A distributed solution must coordinate ordering across partitions.

Use narrower numeric types only when their precision and range preserve meaningful distinctions. Rounding two close values into one tie changes the result. An inplace operation is not a general promise of lower memory use. Measure the complete workload after checking correctness.

For running statistics and previous-row calculations, see pandas window functions. Primary references: DataFrame.rank and Series.rank.

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles