How do I stack DataFrames together with concat in pandas?
Short answer
Use pd.concat to stack DataFrames on top of each other by rows, or side by side by columns with axis=1. It combines by position, unlike merge which combines by matching a key.
The short version
Use pd.concat with a list of DataFrames to stack them. By default it stacks rows (one frame under another). Pass axis=1 to place them side by side as columns. This is different from merge, which lines rows up by matching a key. concat combines by position and index, so reach for it when you are gluing together pieces that already share the same shape or columns.
Stacking rows
import pandas as pd
north = pd.DataFrame({"city": ["Leeds", "Bath"], "sales": [10, 20]})
south = pd.DataFrame({"city": ["York"], "sales": [30]})
combined = pd.concat([north, south], ignore_index=True)
print(combined)
city sales
0 Leeds 10
1 Bath 20
2 York 30
The frames are matched by column name, so the columns do not need to be in the same order. Any column missing from one frame is filled with NaN in the result.
The duplicate-index trap
Without ignore_index=True, concat keeps each frame's original index, so you get repeated index labels.
pd.concat([north, south]).index.tolist() # [0, 1, 0]
That repeated 0 causes confusing results later, because loc[0] now matches two rows. Passing ignore_index=True renumbers the result cleanly, which is almost always what you want when stacking rows.
Stacking columns
With axis=1, frames are joined side by side, aligned on their index.
prices = pd.DataFrame({"price": [10, 20]})
taxes = pd.DataFrame({"tax": [1, 2]})
pd.concat([prices, taxes], axis=1)
Because alignment is by index here, mismatched indexes produce NaN gaps. If the two frames are just parallel lists with no meaningful index, reset the index on both first so they line up by position.
concat versus merge
This is the distinction people trip over. concat combines by position and index, so use it to append more rows of the same shape, or to bolt on more columns that are already aligned. merge combines by matching values in a key column, so use it to bring together related tables, like orders and the customers they belong to. If you find yourself lining up rows by a shared identifier, you want merge, not concat.
A note on the old append method
Older code used df.append(other). That method was removed in recent pandas, so use pd.concat([df, other]) instead, which does the same job and handles a list of frames in one call.
Enjoying these?
Go from piecing together answers to real fluency.
Our courses take you from looking things up to doing the work with confidence, step by step on real data.
Browse the courses