pandas
data analysis
top n records
Python
data manipulation

Pandas get topmost n records within each group

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

Getting the top n rows per group is a common Pandas task in reporting, ranking, and data-cleaning pipelines. The right approach depends on what "top" means in your dataset: first rows after sorting, largest values in a metric column, or tied rows based on a rank rule.

The Most Common Pattern: Sort Then Use groupby().head()

In real projects, the simplest and clearest solution is often to sort the DataFrame first and then take the first n rows from each group.

python
1import pandas as pd
2
3df = pd.DataFrame(
4    {
5        "team": ["A", "A", "A", "B", "B", "B"],
6        "player": ["p1", "p2", "p3", "p4", "p5", "p6"],
7        "score": [18, 25, 20, 30, 27, 21],
8    }
9)
10
11result = (
12    df.sort_values(["team", "score"], ascending=[True, False])
13      .groupby("team", group_keys=False)
14      .head(2)
15)
16
17print(result)

This works because head(2) is applied after the rows inside each group have been ordered by score descending. The output contains the top two scoring players from each team.

This pattern is easy to read and scales well to more than one sort column. For example, you can sort by team, then score descending, then name ascending to make ties deterministic.

When You Only Need the Largest Rows by One Column

If the task is specifically "largest n rows by one numeric column," nlargest is another good option. It can be especially clear when the ranking rule is based on a single metric.

python
1import pandas as pd
2
3df = pd.DataFrame(
4    {
5        "group": ["x", "x", "x", "y", "y", "y"],
6        "item": ["a", "b", "c", "d", "e", "f"],
7        "value": [5, 9, 7, 6, 10, 8],
8    }
9)
10
11result = (
12    df.groupby("group", group_keys=False)
13      .apply(lambda part: part.nlargest(2, columns="value"))
14)
15
16print(result)

This is concise, but there is a tradeoff. apply is flexible, yet it can be slower and harder to reason about than a direct sort-plus-head pipeline on very large data. For many workloads, the sort-based pattern remains the best default.

Use Ranking When You Need More Control Over Ties

Sometimes the business rule is not "exactly two rows." Instead, it is "all rows whose rank is within the top two values." In that case, ranking is clearer than slicing.

python
1import pandas as pd
2
3df = pd.DataFrame(
4    {
5        "store": ["s1", "s1", "s1", "s2", "s2"],
6        "product": ["a", "b", "c", "d", "e"],
7        "sales": [100, 100, 90, 80, 80],
8    }
9)
10
11df["rank"] = df.groupby("store")["sales"].rank(method="dense", ascending=False)
12result = df[df["rank"] <= 2]
13
14print(result)

This method makes tie behavior explicit. With dense ranking, equal values share the same rank. That can produce more than n rows in a group, which is often the correct behavior for leaderboard-style reports.

Preserve or Reset the Index Deliberately

Group operations can produce multi-indexed results if you are not careful. If you want a clean flat table, use group_keys=False during groupby or call reset_index(drop=True) after the selection.

python
1clean = (
2    df.sort_values(["store", "sales"], ascending=[True, False])
3      .groupby("store", group_keys=False)
4      .head(2)
5      .reset_index(drop=True)
6)
7
8print(clean)

Being explicit about the output shape saves time later when the result is merged, exported, or passed to plotting code.

Common Pitfalls

A common mistake is calling groupby().head(n) without sorting first. That returns the first n rows in the original order, not the top n rows by a metric.

Another mistake is using apply for everything. It is powerful, but many top-per-group problems are clearer and faster with sort_values plus head.

Ties are another source of confusion. If you need deterministic results, define the tie-break columns in the sort. If you need all tied rows, use ranking instead of slicing.

Finally, watch the index. Grouped operations can leave a multi-index or preserve original row numbers in ways that make the result look odd when printed.

Summary

  • The usual solution is sort_values(...).groupby(...).head(n).
  • Use nlargest when the rule is explicitly based on one numeric column.
  • Use rank when tie handling matters more than returning exactly n rows.
  • Sort before selecting, or you are not really computing a top-per-group result.
  • Clean up the index intentionally if the output will be reused downstream.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.