Pandas nlargest(): Top Records and Tie Policies

Use nlargest() to select records with the largest values in supported ordering columns. Define the eligible population and decide whether a tie at the cutoff should return exactly n records or every tied record.

Select the largest records with an explicit tie policy 🔝

DataFrame.nlargest(n, columns, keep="first") returns a DataFrame, retaining other fields in each selected record. Series.nlargest(n, keep="first") returns a Series and defaults n to 5; the DataFrame version requires n and columns. Both order selected values descending. keep chooses first, last or all tied occurrences; all may return more than n rows. This selects records, not distinct score levels.

Select two students with the highest marks 🔝

The synthetic student table has one score of 80 and three scores of 70. Default keep="first" chooses Alex at the tied cutoff. Source index labels remain intact.

import pandas as pd
def student_data():
    return pd.DataFrame({"NAME": ["Ravi", "Raju", "Alex", "Ron", "King", "Jack"], "ID": [1, 2, 3, 4, 5, 6], "MATH": [80, 40, 70, 70, 70, 30], "CLASS1": ["Four", "Three", "Three", "Four", "Five", "Three"]})
df = student_data()
result = df.nlargest(n=2, columns="MATH", keep="first")
print(result)
assert result["ID"].tolist() == [1, 3]
assert df["ID"].tolist() == [1, 2, 3, 4, 5, 6]

Expected output

   NAME  ID  MATH CLASS1
0  Ravi   1    80   Four
2  Alex   3    70  Three

Prefer the last tied occurrence 🔝

keep="last" selects King instead of Alex at the cutoff. First and last refer to occurrence order in the input, not smallest or largest ID.

df = student_data()
result = df.nlargest(2, "MATH", keep="last")
print(result)
assert result["ID"].tolist() == [1, 5]

Expected output

   NAME  ID  MATH CLASS1
0  Ravi   1    80   Four
4  King   5    70   Five

Keep every record tied at the cutoff 🔝

keep="all" includes all three students scoring 70 along with Ravi scoring 80. The result has four rows even though n=2. It is not a request for two distinct score levels in general.

df = student_data()
result = df.nlargest(2, "MATH", keep="all")
print(result)
print("Returned records:", len(result))
assert result["ID"].tolist() == [1, 3, 4, 5]
assert len(result) == 4

Expected output

   NAME  ID  MATH CLASS1
0  Ravi   1    80   Four
2  Alex   3    70  Three
3   Ron   4    70   Four
4  King   5    70   Five
Returned records: 4

Return only the score Series 🔝

A Series result contains values and their original index labels, without student fields. Use the DataFrame version when the output must contain full records. See Series and iloc for related concepts.

df = student_data()
scores = df["MATH"].nlargest(2)
print(scores)
assert scores.tolist() == [80, 70]
assert scores.index.tolist() == [0, 2]

Expected output

0    80
2    70
Name: MATH, dtype: int64

Use several descending ordering fields 🔝

All columns supplied to nlargest are ordered descending. Here ENGLISH breaks the MATH tie: King’s English score of 60 wins over Ron’s 50 and Alex’s 40. Use sort_values() for mixed ascending and descending directions.

df = student_data()
df["ENGLISH"] = [80, 70, 40, 50, 60, 30]
result = df.nlargest(2, ["MATH", "ENGLISH"])
print(result)
mixed = df.sort_values(["MATH", "ID"], ascending=[False, True]).head(2)
print("Score descending, ID ascending:")
print(mixed)
assert result["ID"].tolist() == [1, 5]
assert mixed["ID"].tolist() == [1, 3]

Expected output

   NAME  ID  MATH CLASS1  ENGLISH
0  Ravi   1    80   Four       80
4  King   5    70   Five       60
Score descending, ID ascending:
   NAME  ID  MATH CLASS1  ENGLISH
0  Ravi   1    80   Four       80
2  Alex   3    70  Three       40

Parse numeric strings before top-value selection 🔝

Text or categorical ordering columns are unsupported by DataFrame.nlargest and raise TypeError. Parse numbers explicitly, then review failures rather than treating them as zero. Other non-ordering text columns can remain in the result.

raw = pd.DataFrame({"id": [1, 2, 3, 4], "amount": ["100", "20", "3", "bad"]})
try:
    raw.nlargest(2, "amount")
except TypeError:
    print("Parse the amount column before selecting largest values.")
cleaned = raw.copy()
cleaned["amount"] = pd.to_numeric(raw["amount"], errors="coerce")
known = cleaned.loc[cleaned["amount"].notna()].copy()
print(known.nlargest(2, "amount"))
print("Unparseable source values:")
print(raw.loc[cleaned["amount"].isna()])
assert known.nlargest(2, "amount")["id"].tolist() == [1, 2]

Expected output

Parse the amount column before selecting largest values.
   id  amount
0   1   100.0
1   2    20.0
Unparseable source values:
   id amount
3   4    bad

Define the eligible population before choosing n 🔝

Select known values explicitly when every selected row must have a usable measure. If fewer than n eligible records exist, return those available records and report the count. Validate n as a positive integer for a user-facing report.

records = pd.DataFrame({"id": [1, 2, 3], "score": [80, None, 70]})
eligible = records.loc[records["score"].notna()].copy()
n = 5
assert isinstance(n, int) and n > 0
result = eligible.nlargest(n, "score")
print(result)
print("Requested:", n, "Available:", len(result))
assert result["id"].tolist() == [1, 3]
assert result["score"].notna().all()

Expected output

   id  score
0   1   80.0
2   3   70.0
Requested: 5 Available: 2

Choose top records within each group 🔝

For mixed-direction tie-breaking and full rows, sort by class, mark descending and ID ascending, then use groupby().head(). This selects up to two student records in each class; it is different from selecting two records globally.

df = student_data()
ordered = df.sort_values(["CLASS1", "MATH", "ID"], ascending=[True, False, True])
top_by_class = ordered.groupby("CLASS1", sort=False).head(2)
print(top_by_class)
assert top_by_class.groupby("CLASS1").size().max() <= 2
assert set(top_by_class["ID"]) == {1, 3, 4, 5, 2}

Expected output

   NAME  ID  MATH CLASS1
4  King   5    70   Five
0  Ravi   1    80   Four
3   Ron   4    70   Four
2  Alex   3    70  Three
1  Raju   2    40  Three

Inspect the largest paid orders 🔝

The synthetic extract includes a test order, invalid text, tied paid amounts and an ordinary small paid order. Define paid orders with known non-negative amounts before selecting the top two. keep="all" includes both orders tied at the cutoff.

raw = pd.DataFrame({"order_id": [101, 102, 103, 104, 105, 106], "status": ["paid", "paid", "paid", "test", "paid", "paid"], "amount": ["100", "70", "70", "500", "bad", "20"]})
cleaned = raw.copy()
cleaned["amount"] = pd.to_numeric(raw["amount"], errors="coerce")
eligible_mask = cleaned["status"].eq("paid") & cleaned["amount"].ge(0).fillna(False)
eligible = cleaned.loc[eligible_mask].copy()
review = raw.loc[~eligible_mask].copy()
top = eligible.nlargest(2, "amount", keep="all")
print("Top orders including cutoff ties:")
print(top)
print("Excluded source records:")
print(review)
assert top["order_id"].tolist() == [101, 102, 103]
assert len(top) == 3
assert review["order_id"].tolist() == [104, 105]

Expected output

Top orders including cutoff ties:
   order_id status  amount
0       101   paid   100.0
1       102   paid    70.0
2       103   paid    70.0
Excluded source records:
   order_id status amount
3       104   test    500
4       105   paid    bad

Report the denominator and export the selected records 🔝

The top-record subtotal is not total eligible revenue. State the population and tie policy alongside the selected count. This sample’s selected revenue is 240 out of 260 eligible revenue. Unique customers or products require a separate grouping rule.

total = eligible["amount"].sum()
selected = top["amount"].sum()
print("Eligible orders:", len(eligible))
print("Selected orders:", len(top))
print("Eligible revenue:", total)
print("Selected revenue:", selected)
print("Selected revenue share:", round(selected / total * 100, 2))
print(top.to_csv(index=False).rstrip())
assert total == 260 and selected == 240
# Optional Colab export:
# top.to_csv("top_paid_orders.csv", index=False)

Expected output

Eligible orders: 4
Selected orders: 3
Eligible revenue: 260.0
Selected revenue: 240.0
Selected revenue share: 92.31
order_id,status,amount
101,paid,100.0
102,paid,70.0
103,paid,70.0

Rank product summaries rather than individual sales 🔝

If the question is which products have the most revenue, aggregate by product first and select the largest summary rows. Use groupby() for the summary and cut() for bands rather than top records.

sales = pd.DataFrame({"product": ["Pen", "Pen", "Book", "Notebook"], "revenue": [60, 60, 100, 30]})
summary = sales.groupby("product", as_index=False).agg(revenue=("revenue", "sum"))
top_products = summary.nlargest(2, "revenue", keep="all")
print(top_products)
assert top_products["product"].tolist() == ["Pen", "Book"]
assert summary["revenue"].sum() == sales["revenue"].sum()

Expected output

  product  revenue
2     Pen      120
0    Book      100

Common questions 🔝

Why are more than n rows returned? keep="all" includes ties at the cutoff. Does it return n unique values? No, it selects records. Why did a different tied ID win? first and last depend on input order; use explicit secondary sort keys when IDs decide ties. Does it reset the index? No; use reset_index(drop=True) after selection if required. Can it order names? Use sort_values() for text. Does it select one record per customer? No; define a customer-level summary or per-group selection first. Is it faster? It can avoid fully sorting a supported numeric column for a small top-n selection; measure representative data if performance matters. For a report with mixed directions use sort_values().

Exercise: include ties at the second position 🔝

Use IDs 1, 2, 3 and 4 with amounts 90, 60, 60 and 10. Select the largest two records with keep="all". Predict the number of selected records and their subtotal.

Exercise solution 🔝

Three records qualify because two share the cutoff amount of 60.

practice = pd.DataFrame({"id": [1, 2, 3, 4], "amount": [90, 60, 60, 10]})
answer = practice.nlargest(2, "amount", keep="all")
print(answer)
print("Subtotal:", answer["amount"].sum())
assert answer["id"].tolist() == [1, 2, 3]
assert len(answer) == 3
assert answer["amount"].sum() == 210

Expected output

   id  amount
0   1      90
1   2      60
2   3      60
Subtotal: 210

Practice in Google Colab 🔝

Open in Google Colab View on GitHub
Run the examples in order, change n and tie policies, then inspect selected records and report denominators. Save your own copy to keep edits. All sample tables are included in the notebook.

Continue with the Data Cleaning hub, where(), loc, at, NumPy where(), fillna() and iloc.

Reference: Pandas DataFrame.nlargest documentation.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer