Pandas sort_values(): Order Records and Reports

Use sort_values() to order records by scores, amounts, dates or other fields. Choose tie-breaking keys, missing-value placement and index handling so the report order is deliberate and repeatable.

Sort records by values 🔝

sort_values() normally reorders rows using values in one or more columns. It does not independently rearrange each column: the fields in a record stay together. The default is ascending=True and missing values last. by selects the sort fields; axis selects the axis; ascending sets directions; na_position places missing values; ignore_index creates new consecutive labels; key supplies a vectorised sorting transformation. Use sort_index() when you want label order.

Sort student marks in ascending order 🔝

Use kind="stable" for a single-key sort when tied marks should retain their input order. The original table remains unchanged unless you request inplace=True. The displayed index labels still identify the source rows.

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], "ENGLISH": [80, 70, 40, 50, 60, 30]})
df = student_data()
result = df.sort_values("MATH", kind="stable")
print(result)
assert result["ID"].tolist() == [6, 2, 3, 4, 5, 1]
assert df["ID"].tolist() == [1, 2, 3, 4, 5, 6]

Expected output

   NAME  ID  MATH  ENGLISH
5  Jack   6    30       30
1  Raju   2    40       70
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
0  Ravi   1    80       80

Resolve ties with a second column 🔝

Sort first by MATH, then ENGLISH within tied MATH values. A final unique ID key can make the required tie-breaking order explicit for repeatable reports.

df = student_data()
result = df.sort_values(["MATH", "ENGLISH", "ID"])
print(result)
assert result["ID"].tolist() == [6, 2, 3, 4, 5, 1]

Expected output

   NAME  ID  MATH  ENGLISH
5  Jack   6    30       30
1  Raju   2    40       70
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
0  Ravi   1    80       80

Sort scores in descending order 🔝

ascending=False reverses the value ordering, not the source table’s index labels. With a single-key stable sort, tied marks retain their source order.

df = student_data()
result = df.sort_values("MATH", ascending=False, kind="stable")
print(result)
assert result["ID"].tolist() == [1, 3, 4, 5, 2, 6]

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
1  Raju   2    40       70
5  Jack   6    30       30

Use different directions for different fields 🔝

An ascending list must match the number of by fields. This report puts MATH highest first, then ENGLISH lowest first and ID lowest first.

df = student_data()
result = df.sort_values(["MATH", "ENGLISH", "ID"], ascending=[False, True, True])
print(result)
assert result["ID"].tolist() == [1, 3, 4, 5, 2, 6]

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
1  Raju   2    40       70
5  Jack   6    30       30

Sort names and choose case handling 🔝

The student names are sorted in descending text order. For mixed case, key=lambda column: column.str.casefold() creates a case-insensitive comparison key without changing the stored names. This is not a locale-specific collation rule.

df = student_data()
print(df.sort_values("NAME", ascending=False))
names = pd.DataFrame({"name": ["bob", "Alice", "alice", "Carol"]})
result = names.sort_values("name", key=lambda column: column.str.casefold(), kind="stable")
print("Case-insensitive:")
print(result)
assert result["name"].tolist() == ["Alice", "alice", "bob", "Carol"]

Expected output

   NAME  ID  MATH  ENGLISH
3   Ron   4    70       50
0  Ravi   1    80       80
1  Raju   2    40       70
4  King   5    70       60
5  Jack   6    30       30
2  Alex   3    70       40
Case-insensitive:
    name
1  Alice
2  alice
0    bob
3  Carol

Place missing values without filling them 🔝

na_position="first" places missing values first regardless of ascending. Sorting does not validate or fill values. Keep missing-score records visible for review when that is the report’s purpose.

df = student_data()
df["MATH"] = df["MATH"].astype("Int64")
df.loc[1, "MATH"] = pd.NA
result = df.sort_values("MATH", ascending=False, na_position="first", kind="stable")
print(result)
assert result.iloc[0]["ID"] == 2
assert result["MATH"].isna().sum() == 1

Expected output

   NAME  ID  MATH  ENGLISH
1  Raju   2  <NA>       70
0  Ravi   1    80       80
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
5  Jack   6    30       30

Preserve identifiers when resetting labels 🔝

ignore_index=True assigns labels 0 through n-1 after sorting. It does not change an ID column. Use loc for labels and iloc for positions; the first displayed row need not have label 0 unless you reset it.

df = student_data()
result = df.sort_values("MATH", ignore_index=True, kind="stable")
print(result)
assert result.index.tolist() == list(range(6))
assert result.loc[0, "ID"] == 6
assert result.iloc[0]["NAME"] == "Jack"

Expected output

   NAME  ID  MATH  ENGLISH
0  Jack   6    30       30
1  Raju   2    40       70
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
5  Ravi   1    80       80

Parse numbers and dates before sorting 🔝

Text numbers sort lexicographically rather than numerically. Parse and review failures before using a numeric sort. Parse dates with a known source format rather than relying on how the strings look.

raw = pd.DataFrame({"id": [1, 2, 3, 4], "amount": ["100", "20", "3", "bad"], "date": ["02/09/2026", "01/10/2026", "01/09/2026", "bad"]})
cleaned = raw.copy()
cleaned["amount"] = pd.to_numeric(raw["amount"], errors="coerce")
cleaned["date"] = pd.to_datetime(raw["date"], format="%d/%m/%Y", errors="coerce")
print("Text amount order:", raw.sort_values("amount")["id"].tolist())
print("Numeric amount order:")
print(cleaned.sort_values(["amount", "id"], na_position="last"))
print("Date order:", cleaned.sort_values(["date", "id"])["id"].tolist())
assert cleaned.sort_values(["amount", "id"])["id"].tolist() == [3, 2, 1, 4]
assert cleaned.sort_values(["date", "id"])["id"].tolist() == [3, 1, 2, 4]

Expected output

Text amount order: [1, 2, 3, 4]
Numeric amount order:
   id  amount       date
2   3     3.0 2026-09-01
1   2    20.0 2026-10-01
0   1   100.0 2026-09-02
3   4     NaN        NaT
Date order: [3, 1, 2, 4]

Use an ordered category for business priorities 🔝

Alphabetical order may not match a reporting rule. Validate labels before converting them to a fixed categorical dtype; unexpected labels can otherwise become missing. This example puts High before Medium before Low.

tasks = pd.DataFrame({"id": [1, 2, 3, 4], "priority": ["Low", "High", "Medium", "High"]})
order = ["High", "Medium", "Low"]
assert tasks["priority"].isin(order).all()
tasks["priority"] = tasks["priority"].astype(pd.CategoricalDtype(order, ordered=True))
result = tasks.sort_values(["priority", "id"])
print(result)
assert result["id"].tolist() == [2, 4, 3, 1]

Expected output

   id priority
1   2     High
3   4     High
2   3   Medium
0   1      Low

Use inplace on the intended table 🔝

inplace=True updates the DataFrame itself. Explicitly assigning a returned sorted table is often easier to follow, particularly when retaining a raw copy. Do not rely on chained edits to a selected column.

df = student_data()
df.sort_values(["MATH", "ID"], inplace=True)
print(df)
assert df["ID"].tolist() == [6, 2, 3, 4, 5, 1]

Expected output

   NAME  ID  MATH  ENGLISH
5  Jack   6    30       30
1  Raju   2    40       70
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
0  Ravi   1    80       80

Sort columns by values in a row 🔝

axis=1 sorts columns using row labels specified in by. It is a different operation from ordering rows by a column. For alphabetical column labels, use sort_index(axis=1).

scores = pd.DataFrame({"MATH": [70, 80], "ENGLISH": [50, 90], "SCIENCE": [60, 75]}, index=["Alex", "Ravi"])
result = scores.sort_values(by="Alex", axis=1)
print(result)
assert result.columns.tolist() == ["ENGLISH", "SCIENCE", "MATH"]

Expected output

      ENGLISH  SCIENCE  MATH
Alex       50       60    70
Ravi       90       75    80

Prepare a sorted paid-order report 🔝

This synthetic extract has tied amounts, a test order and an invalid amount. Define the report population first, then sort by amount descending, date ascending and order ID ascending. Retain excluded raw records for review.

raw = pd.DataFrame({"order_id": [101, 102, 103, 104, 105, 106], "date": ["2026-09-02", "2026-09-01", "2026-09-03", "2026-09-04", "2026-09-01", "2026-09-01"], "status": ["paid", "paid", "test", "paid", "paid", "paid"], "amount": ["20", "20", "50", "bad", "10", "20"]})
cleaned = raw.copy()
cleaned["date"] = pd.to_datetime(raw["date"], format="%Y-%m-%d", errors="coerce")
cleaned["amount"] = pd.to_numeric(raw["amount"], errors="coerce")
valid = cleaned["status"].eq("paid") & cleaned["date"].notna() & cleaned["amount"].ge(0).fillna(False)
accepted = cleaned.loc[valid].copy()
report = accepted.sort_values(["amount", "date", "order_id"], ascending=[False, True, True], ignore_index=True)
review = raw.loc[~valid].copy()
print("Sorted report:")
print(report)
print("Excluded records:")
print(review)
assert report["order_id"].tolist() == [102, 106, 101, 105]
assert report["amount"].sum() == accepted["amount"].sum() == 70
assert review["order_id"].tolist() == [103, 104]

Expected output

Sorted report:
   order_id       date status  amount
0       102 2026-09-01   paid    20.0
1       106 2026-09-01   paid    20.0
2       101 2026-09-02   paid    20.0
3       105 2026-09-01   paid    10.0
Excluded records:
   order_id        date status amount
2       103  2026-09-03   test     50
3       104  2026-09-04   paid    bad

Select top records and export without changing totals 🔝

head(3) returns exactly three rows after sorting, so the explicit tie-breaking rule determines which rows appear. If all tied top values should be included, define that rule separately; see nlargest(). Sorting alone should preserve record membership and totals.

top = report.head(3)
print("Top three:")
print(top)
print(report.to_csv(index=False).rstrip())
assert set(report["order_id"]) == set(accepted["order_id"])
assert top["amount"].sum() == 60
# Optional Colab export:
# report.to_csv("sorted_paid_orders.csv", index=False)

Expected output

Top three:
   order_id       date status  amount
0       102 2026-09-01   paid    20.0
1       106 2026-09-01   paid    20.0
2       101 2026-09-02   paid    20.0
order_id,date,status,amount
102,2026-09-01,paid,20.0
106,2026-09-01,paid,20.0
101,2026-09-02,paid,20.0
105,2026-09-01,paid,10.0

Common questions 🔝

Why is 100 before 20? Check whether the values are strings. Why did index labels stay out of order? Sorting preserves row labels unless ignore_index=True. Why did tied records change order? Specify a stable single-key sort or explicit secondary keys. kind only applies to a single-column/label DataFrame sort. Does sorting remove duplicates? No; use a separate validated rule. Does sorting fill missing values? No; na_position only chooses their placement. Why did a key function fail? It must accept a Series and return a same-shaped result; for mixed field types, prepare dedicated sort keys. Continue with filtering rows, group summaries and plotting.

Exercise: sort by score and then ID 🔝

Use IDs 3, 1 and 2 with scores 80, 80 and 70. Sort score descending and ID ascending, with a fresh consecutive index. Predict the ID order.

Exercise solution 🔝

ID breaks the tie between the two scores of 80.

practice = pd.DataFrame({"ID": [3, 1, 2], "score": [80, 80, 70]})
answer = practice.sort_values(["score", "ID"], ascending=[False, True], ignore_index=True)
print(answer)
assert answer["ID"].tolist() == [1, 3, 2]
assert answer.index.tolist() == [0, 1, 2]

Expected output

   ID  score
0   1     80
1   3     80
2   2     70

Practice in Google Colab 🔝

Open in Google Colab View on GitHub
Run the examples in order, change sort directions and tied values, then inspect record order and preserved totals. 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.sort_values 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