Pandas - Reorder/Rearrange Columns

The most straightforward approach to reorder columns is passing a list of column names in your desired sequence. This creates a new DataFrame with columns arranged according to your specification.

Key Insights

  • Reordering columns in Pandas can be accomplished through direct column assignment, reindex(), or loc[] accessor, each suited for different scenarios from simple repositioning to complex conditional arrangements
  • Column ordering impacts performance when working with large datasets—placing frequently accessed columns first can improve cache locality and reduce memory access time
  • Understanding the difference between view and copy operations during column reordering prevents unexpected behavior and memory issues in production code

Basic Column Reordering with Direct Assignment

The most straightforward approach to reorder columns is passing a list of column names in your desired sequence. This creates a new DataFrame with columns arranged according to your specification.

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie'],
    'age': [25, 30, 35],
    'city': ['NYC', 'LA', 'Chicago'],
    'salary': [70000, 80000, 90000]
})

# Reorder columns explicitly
df = df[['name', 'city', 'age', 'salary']]
print(df)

This method works well when you know exactly which columns you want and their order. However, it requires typing all column names, making it error-prone for DataFrames with many columns.

Using reindex() for Flexible Column Arrangement

The reindex() method provides more flexibility, especially when dealing with missing or extra column names. It handles non-existent columns gracefully and allows you to specify fill values.

df = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': [7, 8, 9]
})

# Reorder and add new columns
df_reindexed = df.reindex(columns=['C', 'A', 'B', 'D'])
print(df_reindexed)
# Column 'D' will contain NaN values

# Specify fill value for new columns
df_reindexed = df.reindex(columns=['C', 'A', 'B', 'D'], fill_value=0)
print(df_reindexed)

The reindex() method is particularly useful in ETL pipelines where you need to ensure a consistent column schema even when source data varies.

Moving Specific Columns to Front or End

In real-world scenarios, you often need to move one or two columns to the beginning or end while maintaining the relative order of others.

df = pd.DataFrame({
    'col1': [1, 2],
    'col2': [3, 4],
    'col3': [5, 6],
    'col4': [7, 8],
    'col5': [9, 10]
})

# Move 'col3' to the front
cols = df.columns.tolist()
cols = ['col3'] + [col for col in cols if col != 'col3']
df = df[cols]

# Move multiple columns to front
priority_cols = ['col4', 'col1']
other_cols = [col for col in df.columns if col not in priority_cols]
df = df[priority_cols + other_cols]

# Move column to the end
cols = [col for col in df.columns if col != 'col2'] + ['col2']
df = df[cols]

This pattern is essential when preparing data for display or when certain identifier columns must appear first for readability.

Sorting Columns Alphabetically

Alphabetical sorting simplifies column management in DataFrames with numerous columns and makes them easier to navigate.

df = pd.DataFrame({
    'zebra': [1, 2, 3],
    'alpha': [4, 5, 6],
    'beta': [7, 8, 9],
    'gamma': [10, 11, 12]
})

# Sort columns alphabetically
df_sorted = df[sorted(df.columns)]
print(df_sorted)

# Reverse alphabetical order
df_reverse = df[sorted(df.columns, reverse=True)]
print(df_reverse)

# Case-insensitive sorting
df_case_insensitive = df[sorted(df.columns, key=str.lower)]

Alphabetical ordering is particularly valuable in data exploration and when merging DataFrames from different sources that should have standardized column orders.

Conditional Column Ordering Based on Data Types

Advanced use cases require ordering columns based on their data types, which is useful for optimizing memory layout or grouping similar data together.

df = pd.DataFrame({
    'name': ['Alice', 'Bob'],
    'age': [25, 30],
    'salary': [70000.5, 80000.75],
    'active': [True, False],
    'hire_date': pd.to_datetime(['2020-01-01', '2019-06-15'])
})

# Group columns by data type
numeric_cols = df.select_dtypes(include=['int64', 'float64']).columns.tolist()
string_cols = df.select_dtypes(include=['object']).columns.tolist()
bool_cols = df.select_dtypes(include=['bool']).columns.tolist()
datetime_cols = df.select_dtypes(include=['datetime64']).columns.tolist()

# Reorder: strings, then numerics, then booleans, then datetimes
new_order = string_cols + numeric_cols + bool_cols + datetime_cols
df_reordered = df[new_order]
print(df_reordered)

This approach optimizes data access patterns and makes the DataFrame structure more logical for analysis.

Using loc[] for Column Reordering

The loc[] accessor can reorder columns while simultaneously filtering rows, enabling atomic operations that combine row and column manipulation.

df = pd.DataFrame({
    'A': [1, 2, 3, 4],
    'B': [5, 6, 7, 8],
    'C': [9, 10, 11, 12],
    'D': [13, 14, 15, 16]
})

# Reorder columns using loc
df_reordered = df.loc[:, ['C', 'A', 'D', 'B']]

# Combine row filtering with column reordering
df_filtered = df.loc[df['A'] > 2, ['D', 'B', 'A']]
print(df_filtered)

This method is efficient when you need to perform multiple DataFrame transformations in a single operation.

Handling Dynamic Column Lists

When working with programmatically generated column lists, you need robust error handling and validation.

df = pd.DataFrame({
    'id': [1, 2, 3],
    'value_2023': [100, 200, 300],
    'value_2024': [150, 250, 350],
    'category': ['A', 'B', 'C']
})

# Get columns matching a pattern
value_cols = [col for col in df.columns if col.startswith('value_')]
other_cols = [col for col in df.columns if not col.startswith('value_')]

# Reorder with pattern-matched columns first
df = df[value_cols + other_cols]

# Safe reordering with column validation
desired_order = ['id', 'category', 'value_2023', 'value_2024', 'nonexistent']
existing_cols = [col for col in desired_order if col in df.columns]
df = df[existing_cols]
print(df)

This pattern prevents runtime errors when column names change or when working with variable DataFrame schemas.

Performance Considerations

Column reordering creates a new DataFrame, which has memory implications for large datasets. Understanding when to use in-place operations versus creating copies is critical.

import numpy as np

# Create large DataFrame
df = pd.DataFrame(np.random.randn(1000000, 10), 
                  columns=[f'col_{i}' for i in range(10)])

# Memory-efficient reordering for large DataFrames
# Avoid: df = df[new_column_order]  # Creates full copy

# Better: use reindex with copy=False when possible
new_order = [f'col_{i}' for i in range(9, -1, -1)]
df = df.reindex(columns=new_order)

# For truly in-place column renaming and reordering
# Combine rename with column assignment
df.columns = new_order

When reordering columns on DataFrames with millions of rows, the memory overhead of creating copies can be significant. Profile your code to determine if column reordering is a bottleneck.

Reordering with MultiIndex Columns

DataFrames with MultiIndex columns require special handling to maintain the hierarchical structure.

arrays = [
    ['A', 'A', 'B', 'B'],
    ['one', 'two', 'one', 'two']
]
tuples = list(zip(*arrays))
index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])

df = pd.DataFrame(np.random.randn(3, 4), columns=index)

# Reorder level 0
df = df.reindex(columns=['B', 'A'], level=0)

# Reorder complete tuples
new_order = [('B', 'two'), ('B', 'one'), ('A', 'two'), ('A', 'one')]
df = df[new_order]
print(df)

MultiIndex column reordering is common in pivot tables and grouped aggregations where maintaining hierarchical structure is essential.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.