0Pricing
Pandas & NumPy Academy · Lesson

Index Alignment and Reindexing

Align two DataFrames to a common index with reindex(), fill missing labels, and understand how arithmetic aligns indices.

Index Alignment and Reindexing is a free Pandas & NumPy Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Pandas & NumPy Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

How Pandas Aligns Indices

One of Pandas' most powerful — and sometimes surprising — behaviours is automatic index alignment. When you add, subtract, or otherwise combine two Series or DataFrames, Pandas first aligns them on their index labels before performing the operation. Matching labels are operated on; non-matching labels produce NaN. This prevents silent errors from misaligned data and makes data from different sources safe to combine without manually sorting or reordering first.

import pandas as pd

s1 = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
s2 = pd.Series([1, 2, 3], index=['b', 'c', 'd'])

# Alignment in action: a→NaN, d→NaN
result = s1 + s2
print('s1 + s2 with automatic alignment:')
print(result)

NaN from Misaligned Indices

When index labels don't match, Pandas fills the missing entries with NaN. This is intentional: it signals 'I had no corresponding value from one of the operands'. Always inspect the result of arithmetic between two Series or DataFrames for unexpected NaN values — they are a sign of index misalignment. Use fill_value=0 in the arithmetic method (s1.add(s2, fill_value=0)) to treat missing aligned values as zero instead of propagating NaN.

import pandas as pd

s1 = pd.Series({'a': 10, 'b': 20, 'c': 30})
s2 = pd.Series({'b': 1, 'c': 2, 'd': 3})

# Default: NaN where labels don't match
print('Default (NaN for unmatched):')
print(s1 + s2)

# fill_value=0: treat missing as zero
print('\nWith fill_value=0:')
print(s1.add(s2, fill_value=0))

DataFrame Arithmetic Alignment

Alignment applies to both rows and columns when combining two DataFrames. The result has the union of both index sets and both column sets, with NaN wherever either DataFrame had no value. This is equivalent to a full outer join on index and columns simultaneously. It means you can safely add DataFrames from different fiscal periods — months that exist in one but not the other get NaN, which you can then fill or drop.

import pandas as pd

df1 = pd.DataFrame({'revenue': [100, 200], 'cost': [80, 150]}, index=['Jan', 'Feb'])
df2 = pd.DataFrame({'revenue': [120, 210], 'headcount': [5, 6]}, index=['Feb', 'Mar'])

result = df1 + df2
print('Combined DataFrame (union of index and columns):')
print(result)

The reindex() Method

df.reindex(new_index) returns a new DataFrame conformed to the new_index label list. Labels that exist in both the original and new index are preserved; labels in new_index but not in the original get NaN; labels in the original but not in new_index are dropped. This is the explicit way to align a DataFrame to a target label set before performing operations.

import pandas as pd

s = pd.Series({'a': 10, 'b': 20, 'c': 30})

# Reindex to a new label set
reindexed = s.reindex(['b', 'c', 'd', 'e'])
print('Original:', s.index.tolist())
print('New index: [b, c, d, e]')
print('\nReindexed Series:')
print(reindexed)
# b, c preserved; d, e → NaN; a dropped

Filling Missing Values After Reindex

reindex() accepts a fill_value parameter to substitute a constant for new labels, and a method parameter for forward fill ('ffill') or backward fill ('bfill'). These fill methods are most useful for time series data where you reindex a Series to a complete date range and want missing days filled with the last known value rather than NaN.

import pandas as pd

# Sparse daily data with gaps
s = pd.Series(
    [10, 20, 30],
    index=pd.to_datetime(['2024-01-01', '2024-01-03', '2024-01-07'])
)

# Reindex to complete date range
full_range = pd.date_range('2024-01-01', '2024-01-07')
print('Forward fill (carry last known value):')
print(s.reindex(full_range, method='ffill'))

print('\nFill with 0:')
print(s.reindex(full_range, fill_value=0))

Reindexing Columns

reindex works on columns too: df.reindex(columns=['col1', 'col2', 'new_col']). New columns that don't exist in the original DataFrame are added with NaN. Existing columns not in the new list are dropped. This is useful for enforcing a canonical column schema across multiple DataFrames that come from different sources and may have inconsistent column sets.

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob'],
    'age': [30, 25],
    'city': ['NY', 'LA']
})

# Enforce a canonical column order and add missing columns
canonical_cols = ['name', 'age', 'email', 'city', 'country']
df_reindexed = df.reindex(columns=canonical_cols)
print(df_reindexed)
# 'email' and 'country' are new → NaN

Using align() to Synchronise Two Objects

s1.align(s2) returns two new objects with the same index (union or intersection based on the join parameter), making them ready for element-wise operations. This is equivalent to reindexing both objects simultaneously to the same label set. Use join='inner' to keep only common labels, or join='outer' (default) to keep all labels from both with NaN for mismatches.

import pandas as pd

s1 = pd.Series({'a': 10, 'b': 20, 'c': 30})
s2 = pd.Series({'b': 1, 'c': 2, 'd': 3})

# Align to common labels only (inner join)
s1_aligned, s2_aligned = s1.align(s2, join='inner')
print('Inner-aligned s1:')
print(s1_aligned)
print('Inner-aligned s2:')
print(s2_aligned)

print('\nProduct on common labels:')
print(s1_aligned * s2_aligned)

Alignment in merge vs. Arithmetic

Pandas offers two philosophies for combining DataFrames: merge/join (SQL-style, explicit key matching) and arithmetic with index alignment (implicit, label-based). Use merge when combining structured tables with explicit key columns. Use arithmetic alignment when performing calculations between DataFrames that should naturally share an index — for example, subtracting a cost DataFrame from a revenue DataFrame both indexed by (region, month).

import pandas as pd

# Two DataFrames sharing the same index
revenue = pd.Series({'North': 500, 'South': 300, 'East': 450})
costs = pd.Series({'North': 350, 'South': 280, 'West': 400})

# Automatic alignment: 'East' and 'West' don't match → NaN
profit = revenue - costs
print('Profit by region:')
print(profit)

# If you want only common regions
profit_inner = revenue.align(costs, join='inner')[0] - revenue.align(costs, join='inner')[1]
print('\nProfit (common regions only):')
print(profit_inner)

Checking Index Equality Before Operations

Before performing operations on two DataFrames, check whether their indices match with (df1.index == df2.index).all(). If indices differ only in order, sort both before comparing. For DataFrames loaded from different sources, it is good practice to explicitly align or reindex before arithmetic to avoid accidental NaN propagation. Document any alignment assumptions in comments or pipeline logs.

import pandas as pd

df1 = pd.DataFrame({'sales': [100, 200, 300]}, index=['Q1', 'Q2', 'Q3'])
df2 = pd.DataFrame({'cost': [80, 160, 240]}, index=['Q1', 'Q2', 'Q3'])

# Check index equality
if (df1.index == df2.index).all():
    print('Indices match — safe to combine.')
    combined = pd.concat([df1, df2], axis=1)
    combined['profit'] = combined['sales'] - combined['cost']
    print(combined)
else:
    print('Indices differ — align before combining!')

Practical Pattern: Standardising Shapes

A common pattern when loading data from multiple files or API calls is that each batch covers a different subset of keys. Before concatenating or aggregating, reindex all batches to the full known key space with fill_value=0. This ensures every batch has the same shape (same index, same columns) before operations, preventing silent shape mismatches that cause wrong aggregated results.

import pandas as pd

# Two sales batches with different product sets
batch1 = pd.Series({'laptop': 50, 'phone': 80, 'tablet': 30})
batch2 = pd.Series({'phone': 90, 'tablet': 40, 'headphones': 60})

# Full product catalogue
all_products = ['laptop', 'phone', 'tablet', 'headphones']

# Standardise both to the full catalogue
b1 = batch1.reindex(all_products, fill_value=0)
b2 = batch2.reindex(all_products, fill_value=0)

total = b1 + b2
print('Total sales per product:')
print(total)

Index Alignment Edge Cases

Two important edge cases: Duplicate labels — if both Series have duplicate index labels, alignment produces a Cartesian-product-like expansion with many NaN values (Pandas does not assume which duplicates correspond to each other). Always ensure unique indices before arithmetic alignment. Integer vs. object indices — a RangeIndex(0,5) and an Int64Index([0,1,2,3,4]) may not align perfectly after operations because one is inferred and the other explicit. Use reset_index(drop=True) to normalise.

import pandas as pd

# Duplicate label alignment — produces unexpected expansion
s1 = pd.Series([1, 2, 3], index=['a', 'a', 'b'])
s2 = pd.Series([10, 20], index=['a', 'b'])

result = s1 + s2
print('Result with duplicate indices (unexpected expansion):')
print(result)
print('\nAlways ensure unique indices before arithmetic!')

Quick Check

Test your understanding of index alignment and reindexing from this lesson.

Lesson Recap

In this lesson you learned: Pandas performs automatic index alignment on arithmetic, producing NaN for unmatched labels, reindex() conforms a DataFrame to a target label set with fill options for missing labels, and align() synchronises two objects to the same index simultaneously. Next up we explore the performance benefits of sorted indices and how to measure them with timeit.

Frequently asked questions

Is the “Index Alignment and Reindexing” lesson free?

Yes — the full text of “Index Alignment and Reindexing” is free to read here on the web, and the Pandas & NumPy Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Pandas & NumPy Academy course, upgrade to CoddyKit PRO.

What will I learn in “Index Alignment and Reindexing”?

Align two DataFrames to a common index with reindex(), fill missing labels, and understand how arithmetic aligns indices. You practise Pandas & NumPy Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Pandas & NumPy Academy?

No prior experience is required. Pandas & NumPy Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Index Alignment and Reindexing” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Pandas & NumPy Academy lesson?

Yes. Every Pandas & NumPy Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Creating a MultiIndex
  2. Selecting Data from a MultiIndex
  3. Index Alignment and Reindexing
  4. Performance Benefits of Sorted Indices
← Back to Pandas & NumPy Academy