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.