python
pandas
data analysis
groupby
sorting

pandas groupby, then sort within groups

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Sorting within groups is a very common pandas task: keep rows grouped by a key, but order the rows inside each group by another column. The important detail is that groupby itself does not perform that within-group sort for you.

The Fastest Pattern Is Usually sort_values

If you want rows grouped by one column and sorted inside each group by another column, the cleanest answer is often to sort the whole DataFrame by both keys at once.

python
1import pandas as pd
2
3df = pd.DataFrame(
4    {
5        "team": ["A", "A", "B", "B", "A"],
6        "player": ["Ann", "Ben", "Cara", "Dan", "Eli"],
7        "score": [14, 21, 9, 25, 18],
8    }
9)
10
11result = df.sort_values(["team", "score"], ascending=[True, False])
12print(result)

That produces all A rows together and all B rows together, while sorting scores within each team from highest to lowest. For many real workloads, this is the most efficient and readable solution.

What groupby Does and Does Not Do

Pandas documents that groupby preserves the order of rows within each group. The sort parameter controls the order of the group keys, not a secondary sort inside each group.

So this:

python
grouped = df.groupby("team", sort=False)

does not sort rows by score inside each team. It only controls whether the group labels themselves are sorted.

Use groupby().apply() for Custom Group Logic

If each group needs its own more complex sorting logic, use groupby with apply:

python
1result = (
2    df.groupby("team", group_keys=False)
3      .apply(lambda group: group.sort_values(["score", "player"], ascending=[False, True]))
4)
5
6print(result)

This approach is more flexible because the function can do anything per group. The tradeoff is that it is often slower than a single sort_values on the whole DataFrame.

Getting the Top Rows per Group

A very common follow-up is "sort within groups, then keep the top n rows from each group." The pattern is sort first, then use groupby().head().

python
1top_two = (
2    df.sort_values(["team", "score"], ascending=[True, False])
3      .groupby("team")
4      .head(2)
5)
6
7print(top_two)

If you want a clean sequential index after that operation, finish with .reset_index(drop=True). Otherwise, pandas preserves the original row labels from the pre-sorted DataFrame, which is often correct but sometimes confusing during reporting, exports, and quick notebook inspection.

This is both concise and efficient. It avoids custom Python loops and expresses the intent clearly.

Stable Tie-Breaking Matters

If the sort column can tie, add a secondary key so the result is deterministic. Otherwise, tied rows may appear in an order that depends on their prior position.

For example:

python
1result = df.sort_values(
2    ["team", "score", "player"],
3    ascending=[True, False, True]
4)

That gives you a stable, readable ordering rule instead of relying on accident.

Common Pitfalls

The most common mistake is assuming groupby automatically sorts rows inside each group. It does not. It only partitions the data.

Another issue is using apply for a simple task that sort_values already handles globally. That adds overhead and makes the code harder to read.

Be careful with the index as well. After groupby().apply(), the result may carry grouped index labels unless you set group_keys=False or reset the index afterward.

Summary

  • 'groupby does not sort rows within each group for you.'
  • For most cases, sort the whole DataFrame with sort_values on the group key and the within-group key.
  • Use groupby(...).apply(...) only when each group needs custom logic.
  • For top n rows per group, sort first and then use groupby().head(n).
  • Add a tie-breaker column when you need deterministic ordering inside groups.

Course illustration
Course illustration

All Rights Reserved.