Back to Blog
Python

Python pandas sort_values, sort_index, and rank

python pandas sort values sort index and ranking: Learn how to use pandas sort_values, sort_index, and rank to reorder and score DataFrame rows, handle ties and missin...

pandasdataframedata-sortingdata-rankingdata-analysis
Illustration of a pandas DataFrame with rows reordered by a sorting arrow and a numbered rank column.

python pandas sort values sort index and ranking requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

Sorting and ranking are fundamental operations in pandas. sort_values() reorders rows by column values, sort_index() reorders rows by their index labels, and rank() assigns a numeric position to each value. Understanding how python pandas sort values, sort index, and ranking differ — and when to combine them — is essential for building correct data pipelines.

Sorting by Column Values with sort_values

DataFrame.sort_values() reorders rows based on the values in one or more columns. The by parameter names the column (or columns) to sort by.

import pandas as pd df = pd.DataFrame({ "name": ["ada", "grace", "alan", "linus"], "score": [92, 88, 95, 88], "group": ["a", "b", "a", "b"] }) df_sorted = df.sort_values("score")

This returns a new DataFrame ordered by score ascending. The original df is unchanged because sort_values returns a copy by default.

The main parameters:

ParameterPurposeDefault
byColumn name or list of names to sort byrequired
ascendingBoolean, or list of booleans for each keyTrue
na_positionWhere missing values are placed'last'
kindSorting algorithm'quicksort'
inplaceModify the caller instead of returning a copyFalse
ignore_indexReset the index to 0..n-1 after sortingFalse

For descending order, pass ascending=False:

df.sort_values("score", ascending=False)

When by receives a list, the first column is the primary sort key:

df.sort_values(["group", "score"], ascending=[True, False])

This sorts by group ascending, then by score descending within each group. The ascending list must match the length of the by list.

Sorting by Index Labels with sort_index

sort_index() reorders rows by their index labels rather than by column values. This is useful when the index carries meaningful order, such as dates or identifiers.

df.set_index("name").sort_index()

This sorts the rows alphabetically by the name index. For a Series, the same method works directly:

df.set_index("name")["score"].sort_index()

With a MultiIndex, the level parameter selects which level to sort by:

df_multi = df.set_index(["group", "name"]) df_multi.sort_index(level="name")

By default, sort_remaining=True sorts the remaining levels after the selected level. Set it to False to keep the other levels in their original order.

The axis parameter controls whether rows or columns are sorted. axis=1 sorts column labels:

df.sort_index(axis=1)

sort_index shares the ascending, kind, na_position, inplace, and ignore_index parameters with sort_values.

Ranking Values with rank()

rank() assigns a numeric rank to each value in a Series or DataFrame, based on the value's position in sorted order. It does not reorder the data; it adds a score.

df["rank"] = df["score"].rank(ascending=False)

By default, ties receive the average of the ranks they would occupy. The method parameter controls this behavior:

methodTie behavior
averageMean of the ranks that would be assigned
minLowest rank in the tied group
maxHighest rank in the tied group
firstRank in order of appearance
denseLike min, but the next rank is incremented by one
df["dense_rank"] = df["score"].rank(method="dense", ascending=False)

With method="dense", the highest score gets rank 1, the next distinct score gets rank 2, and so on, with no gaps. This is the behavior most people expect from a "dense rank" in SQL.

pct=True returns a percentile rank between 0 and 1:

df["pct_rank"] = df["score"].rank(pct=True)

Missing values are kept as NaN by default. na_option="bottom" ranks NaN as the lowest value, and na_option="top" ranks it as the highest.

Sorting by Multiple Keys and Stable Sort Behavior

When sorting by multiple columns, the order of the by list determines the priority. But the sorting algorithm also matters when you sort in separate steps.

The default kind="quicksort" is not stable. If you sort a DataFrame by score and then by group, the second sort can reorder rows that share the same group value, destroying the score order. To preserve the first sort's order within ties of the second sort, use a stable algorithm:

df.sort_values("score", kind="stable").sort_values("group", kind="stable")

Here the second sort keeps the relative order of rows with equal group values, so the score order survives within each group. kind="mergesort" and kind="stable" are the only stable options.

For a single sort_values call with a list of keys, the algorithm applies the keys in order internally, so stability is less of a concern. The multi-step pattern above is only needed when the sort keys come from different sources or must be applied sequentially.

Handling Missing Values and Edge Cases

Missing values interact with sorting and ranking in ways that are easy to overlook.

na_position controls where NaN goes during a sort. The default 'last' places missing values at the end of an ascending sort. For descending sorts, 'last' still means the end of the result, so NaN appears at the bottom:

df.sort_values("score", ascending=False, na_position="first")

For ranking, na_option="keep" (the default) leaves NaN as NaN. na_option="bottom" assigns NaN the lowest rank, and na_option="top" assigns it the highest.

A common error is sorting a column that contains mixed types. If a column holds both strings and numbers, pandas may raise a TypeError during comparison. Ensure the column has a single dtype before sorting.

When the original index is not meaningful, ignore_index=True produces a clean sequential index in one step:

df.sort_values("score", ignore_index=True)

This avoids the separate reset_index(drop=True) call and prevents the old index from being carried into the result.

Performance and Memory Considerations

sort_values and sort_index return a new object by default. For large DataFrames, this means a full copy of the data. inplace=True modifies the caller and avoids that copy, but it returns None, which makes chaining impossible and can hide bugs:

df.sort_values("score", inplace=True)

Prefer assignment for clarity:

df = df.sort_values("score")

The kind parameter affects both speed and memory. quicksort is generally the fastest, while mergesort and stable use more memory but guarantee stability. For a one-off sort of a few thousand rows, the difference is negligible; for repeated sorts inside a loop, choosing the right algorithm matters more.

ignore_index=True is cheap because it only replaces the index array after sorting. It is preferable to calling reset_index(drop=True) afterward, which does the same work in a separate pass.

Ranking Within Groups and Combining Sort with Rank

A common pattern is ranking values within each group, then sorting by that rank. groupby().rank() computes the rank independently for each group:

df["group_rank"] = df.groupby("group")["score"].rank(method="dense", ascending=False)

The result is a Series aligned to the original index, so it can be assigned directly as a new column. Sorting by the group and the rank produces a per-group leaderboard:

df.sort_values(["group", "group_rank"])

This works because sort_values treats the group as the primary key and the rank as the secondary key. The combination of groupby().rank() and sort_values() is the standard way to produce ranked, ordered output in pandas.

When the rank must reflect the final sorted order rather than the original row order, sort first and then rank:

df_sorted = df.sort_values("score", ascending=False, ignore_index=True) df_sorted["final_rank"] = df_sorted["score"].rank(method="first", ascending=False)

method="first" assigns ranks in order of appearance, so after sorting, the first row gets rank 1, the second rank 2, and so on. This is the correct approach when the rank must match the displayed row order exactly.

python pandas sort values sort index and ranking: Practical | RYUSLOG DEV