Skip to main content

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')
ParameterDescriptionDefault
axis0 to sort rows by their index labels; 1 to sort columns by their names0
ascendingTrue for ascending (A→Z, 0→9); False for descendingTrue
inplaceIf True, modifies the original DataFrame; if False, returns a new oneFalse
kindSorting 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
tip

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)
danger

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

GoalCode
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 columnsdf.sort_index().sort_index(axis=1)
Sort permanentlydf.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.