Sort, rank, and top-n

sort_values, sort_index, nlargest, rank, and reset_index.

Sorting does not change the underlying data until you assign the result. Rank assigns competition numbers. Top-n is a sort plus a head.

Goal

Sort by one or more columns, rank within the table, and pull the largest rows without a full shuffle you do not need.

sort_values

df = pd.DataFrame(
    {
        "city": ["Nairobi", "Mombasa", "Kisumu", "Nakuru", "Eldoret"],
        "units": [12, 9, 11, 8, 6],
        "revenue": [126.0, 94.5, 242.0, 176.0, 63.0],
    }
)
print(df.sort_values("revenue"))
print()
print(df.sort_values("revenue", ascending=False))
print()
print(df.sort_values(["units", "city"], ascending=[False, True]))

na_position="first" or "last" controls where missing values go.

Sort the index

df = pd.DataFrame(
    {
        "city": ["Nairobi", "Mombasa", "Kisumu"],
        "units": [12, 9, 11],
    }
)
named = df.set_index("city")
print(named.sort_index())

Top and bottom

df = pd.DataFrame(
    {
        "city": ["Nairobi", "Mombasa", "Kisumu", "Nakuru", "Eldoret"],
        "units": [12, 9, 11, 8, 6],
        "revenue": [126.0, 94.5, 242.0, 176.0, 63.0],
    }
)
print(df.nlargest(3, "revenue"))
print()
print(df.nsmallest(2, "units"))

nlargest is clearer than sort_values(...).head(...) when that is all you want.

Rank

df = pd.DataFrame(
    {
        "city": ["Nairobi", "Mombasa", "Kisumu", "Nakuru", "Eldoret"],
        "revenue": [126.0, 94.5, 242.0, 176.0, 63.0],
    }
)
df["rev_rank"] = df["revenue"].rank(ascending=False)
df["rev_rank_min"] = df["revenue"].rank(ascending=False, method="min")
df["rev_rank_dense"] = df["revenue"].rank(ascending=False, method="dense")
print(df.sort_values("rev_rank"))
  • average (default) — ties share the mean rank
  • min — ties get the best (smallest) rank
  • dense — like min, but no gaps after ties

Reset the index after a filter/sort

df = pd.DataFrame(
    {
        "city": ["Nairobi", "Mombasa", "Kisumu", "Nakuru", "Eldoret"],
        "revenue": [126.0, 94.5, 242.0, 176.0, 63.0],
    }
)
top = df.sort_values("revenue", ascending=False).head(3)
print(top)
print()
print(top.reset_index(drop=True))

drop=True throws away the old row numbers. Leave it False if those labels still mean something.

Tip

groupby(...).head(n) takes the first n rows per group in current order. Sort inside the group first if you want per-city top products.