Python Pandas: How to Sort a Pandas DataFrame by Both Index and Column
Sorting a DataFrame by its structural labels - row index and column names - is essential for organizing data consistently. The sort_index() method in Pandas handles both operations depending on which axis you specify.
This guide explains how to sort by row index, by column names, and by both simultaneously, with clear examples showing the before and after states.
Understanding sort_index() Syntax
DataFrame.sort_index(axis=0, ascending=True, inplace=False, kind='quicksort')
| Parameter | Description | Default |
|---|---|---|
axis | 0 to sort rows by their index labels; 1 to sort columns by their names | 0 |
ascending | True for ascending (A→Z, 0→9); False for descending | True |
inplace | If True, modifies the original DataFrame; if False, returns a new one | False |
kind | Sorting algorithm: 'quicksort', 'mergesort', 'heapsort' | 'quicksort' |
Sample DataFrame
All examples use this DataFrame where both the row index and column names are intentionally out of order:
import pandas as pd
df = pd.DataFrame(
{'col2': [150, 170, 160],
'col1': [70, 55, 60]},
index=[3, 2, 1]
)
print(df)
Output:
col2 col1
3 150 70
2 170 55
1 160 60
The row index is [3, 2, 1] (descending) and the columns are ['col2', 'col1'] (reverse alphabetical).
Sorting by Row Index
Setting axis=0 (or omitting the parameter since it's the default) sorts rows by their index labels:
import pandas as pd
df = pd.DataFrame(
{'col2': [150, 170, 160],
'col1': [70, 55, 60]},
index=[3, 2, 1]
)
sorted_by_index = df.sort_index(axis=0)
print(sorted_by_index)
Output:
col2 col1
1 160 60
2 170 55
3 150 70
Rows are now ordered by index: 1, 2, 3. The column order remains unchanged.
Descending Order
import pandas as pd
df = pd.DataFrame(
{'col2': [150, 170, 160],
'col1': [70, 55, 60]},
index=[3, 2, 1]
)
sorted_desc = df.sort_index(axis=0, ascending=False)
print(sorted_desc)
Output:
col2 col1
3 150 70
2 170 55
1 160 60
Sorting by Column Names
Setting axis=1 sorts the columns alphabetically by their names:
import pandas as pd
df = pd.DataFrame(
{'col2': [150, 170, 160],
'col1': [70, 55, 60]},
index=[3, 2, 1]
)
sorted_by_columns = df.sort_index(axis=1)
print(sorted_by_columns)
Output:
col1 col2
3 70 150
2 55 170
1 60 160
The columns are now in alphabetical order: col1, col2. The row order remains unchanged.
Sorting by Both Index and Column Names
To sort by both axes, chain two sort_index() calls - one for rows and one for columns:
import pandas as pd
df = pd.DataFrame(
{'col2': [150, 170, 160],
'col1': [70, 55, 60]},
index=[3, 2, 1]
)
print("Original DataFrame:")
print(df)
# Sort rows by index, then columns by name
sorted_both = df.sort_index(axis=0).sort_index(axis=1)
print("\nSorted by both index and column names:")
print(sorted_both)
Output:
Original DataFrame:
col2 col1
3 150 70
2 170 55
1 160 60
Sorted by both index and column names:
col1 col2
1 60 160
2 55 170
3 70 150
Both the row index (1, 2, 3) and column names (col1, col2) are now in ascending alphabetical/numerical order.
Temporary vs. Permanent Sorting
By default, sort_index() returns a new DataFrame and leaves the original unchanged. Use inplace=True to modify the original directly.
Temporary Sort (Default)
import pandas as pd
df = pd.DataFrame(
{'col2': [150, 170, 160],
'col1': [70, 55, 60]},
index=[3, 2, 1]
)
# This returns a new sorted DataFrame, df is NOT modified
result = df.sort_index(axis=1)
print("Result (sorted):")
print(result)
print("\nOriginal df (unchanged):")
print(df)
Output:
Result (sorted):
col1 col2
3 70 150
2 55 170
1 60 160
Original df (unchanged):
col2 col1
3 150 70
2 170 55
1 160 60
Permanent Sort with inplace=True
import pandas as pd
df = pd.DataFrame(
{'col2': [150, 170, 160],
'col1': [70, 55, 60]},
index=[3, 2, 1]
)
# Modify the original DataFrame permanently
df.sort_index(axis=1, inplace=True)
print("Original df (now modified):")
print(df)
Output:
Original df (now modified):
col1 col2
3 70 150
2 55 170
1 60 160
Use inplace=True when you are certain you want to permanently reorder the DataFrame. For exploratory analysis, it's safer to assign the result to a new variable so you can always reference the original order.
Practical Example: Sorting a Real-World DataFrame
Here is a more realistic example with string indices and multiple columns:
import pandas as pd
sales = pd.DataFrame({
'Revenue': [50000, 62000, 48000, 71000],
'Expenses': [30000, 35000, 28000, 40000],
'Profit': [20000, 27000, 20000, 31000]
}, index=['Q3', 'Q1', 'Q4', 'Q2'])
print("Original:")
print(sales)
# Sort by quarter (index) and alphabetically by column name
sorted_sales = sales.sort_index(axis=0).sort_index(axis=1)
print("\nSorted by both:")
print(sorted_sales)
Output:
Original:
Revenue Expenses Profit
Q3 50000 30000 20000
Q1 62000 35000 27000
Q4 48000 28000 20000
Q2 71000 40000 31000
Sorted by both:
Expenses Profit Revenue
Q1 35000 27000 62000
Q2 40000 31000 71000
Q3 30000 20000 50000
Q4 28000 20000 48000
Quarters are now in order (Q1–Q4) and columns are alphabetical (Expenses, Profit, Revenue).
Common Mistake: Forgetting to Capture the Result
A frequent error is calling sort_index() without assigning the result or using inplace=True:
import pandas as pd
df = pd.DataFrame({'B': [3, 1], 'A': [2, 4]}, index=[2, 1])
# WRONG: sort_index returns a new DataFrame, but the result is discarded
df.sort_index(axis=1)
print("df is still unsorted:")
print(df)
Output:
df is still unsorted:
B A
2 3 2
1 1 4
The correct approach
Either assign the result or use inplace=True:
# Option 1: Assign the result
df = df.sort_index(axis=1)
# Option 2: Modify in place
df.sort_index(axis=1, inplace=True)
sort_index() does not modify the DataFrame by default. If you call it without assigning the return value or setting inplace=True, the sorted result is silently discarded and the original DataFrame remains unchanged.
Quick Reference
| Goal | Code |
|---|---|
| Sort rows by index (ascending) | df.sort_index() |
| Sort rows by index (descending) | df.sort_index(ascending=False) |
| Sort columns by name (ascending) | df.sort_index(axis=1) |
| Sort columns by name (descending) | df.sort_index(axis=1, ascending=False) |
| Sort both rows and columns | df.sort_index().sort_index(axis=1) |
| Sort permanently | df.sort_index(inplace=True) |
| Sort by column values (not names) | df.sort_values(by='column_name') |
The sort_index() method gives you straightforward control over the ordering of your DataFrame's structural labels. By specifying the axis and direction, you can organize rows by index, columns by name, or both - ensuring your data is presented in a clean, predictable order.