Python Pandas: How to Efficiently Add a Row at the Top of a Pandas DataFrame
Adding rows to the beginning of a DataFrame is a common operation, but it requires understanding how Pandas works internally. DataFrames are column-oriented and immutable in size, meaning every row addition creates a new object rather than modifying the existing one in place. Choosing the right approach prevents performance issues, avoids deprecated method warnings, and keeps your code clean.
In this guide, you will learn how to prepend rows using the recommended pd.concat() approach, understand why some alternatives should be avoided, and recognize the critical performance difference between adding rows one at a time versus in batch.
The Standard Solution: pd.concat()
Create a single-row DataFrame for the new data and concatenate it before the original DataFrame:
import pandas as pd
df = pd.DataFrame({
'Name': ['Alice', 'Bob'],
'Age': [25, 30]
})
# Create the new row as a DataFrame
new_row = pd.DataFrame([{'Name': 'Charlie', 'Age': 35}])
# Concatenate with the new row first
df_updated = pd.concat([new_row, df], ignore_index=True)
print(df_updated)
Output:
Name Age
0 Charlie 35
1 Alice 25
2 Bob 30
The new row appears at the top because it comes first in the pd.concat() call. The ignore_index=True parameter resets the index to a clean sequential sequence (0, 1, 2) instead of preserving the original index values from each DataFrame.
Adding Multiple Rows at Once
The same pattern works for prepending several rows:
import pandas as pd
df = pd.DataFrame({'Name': ['Alice'], 'Age': [25]})
new_rows = pd.DataFrame([
{'Name': 'Bob', 'Age': 30},
{'Name': 'Charlie', 'Age': 35}
])
df_updated = pd.concat([new_rows, df], ignore_index=True)
print(df_updated)
Output:
Name Age
0 Bob 30
1 Charlie 35
2 Alice 25
To add rows to the bottom instead of the top, simply reverse the order: pd.concat([df, new_rows], ignore_index=True).
Alternative: Using .loc[] with an Index Trick
For simple cases with integer-indexed DataFrames, you can assign to index position -1 and then sort:
import pandas as pd
df = pd.DataFrame({'Name': ['Alice', 'Bob'], 'Age': [25, 30]})
# Assign to index -1
df.loc[-1] = ['Charlie', 35]
# Sort to bring -1 to the top, then reset the index
df = df.sort_index().reset_index(drop=True)
print(df)
Output:
Name Age
0 Charlie 35
1 Alice 25
2 Bob 30
This method relies on the fact that -1 sorts before 0 in integer ordering. It can cause unexpected results with non-integer indices or custom index values, and the sort step adds overhead. Prefer pd.concat() for reliability and clarity.
Methods to Avoid
The Removed .append() Method
The .append() method was removed in Pandas 2.0. Code that uses it will raise an error in modern versions:
import pandas as pd
df = pd.DataFrame({'Name': ['Alice', 'Bob'], 'Age': [25, 30]})
try:
df = df.append({'Name': 'Charlie', 'Age': 35}, ignore_index=True)
except AttributeError as e:
print(f"Error: {e}")
Output:
Error: 'DataFrame' object has no attribute 'append'
Replace any .append() calls with pd.concat() as shown in the standard solution above.
Critical Performance Consideration: Avoid Loops
Adding rows inside a loop is one of the most common performance mistakes in Pandas. Each pd.concat() call copies all existing data into a new DataFrame, so adding n rows one at a time results in quadratic (O(n squared)) time complexity:
import pandas as pd
import time
# Wrong: concat inside the loop (extremely slow)
start = time.time()
df = pd.DataFrame(columns=['Name', 'Age'])
for i in range(1000):
new_row = pd.DataFrame([{'Name': f'User_{i}', 'Age': 20 + i}])
df = pd.concat([new_row, df], ignore_index=True)
slow_time = time.time() - start
# Correct: collect first, create once (fast)
start = time.time()
rows = []
for i in range(1000):
rows.append({'Name': f'User_{i}', 'Age': 20 + i})
df = pd.DataFrame(rows)
fast_time = time.time() - start
print(f"Loop concat: {slow_time:.4f}s")
print(f"Batch create: {fast_time:.4f}s")
print(f"Batch is ~{slow_time / fast_time:.0f}x faster")
Output:
Loop concat: 0.5423s
Batch create: 0.0012s
Batch is ~452x faster
The batch approach collects all data in a plain Python list, then creates the DataFrame in a single operation. This changes the complexity from O(n squared) to O(n).
This principle applies whether you are adding rows to the top, bottom, or middle of a DataFrame. Always collect data in a list first, then create or concatenate once at the end.
When Prepending vs. Appending Matters
If you need the new rows at the top of the final result but are collecting data in a loop, it is more efficient to collect everything first and then control the order in a single pd.concat() call:
import pandas as pd
existing_df = pd.DataFrame({
'Name': ['Alice', 'Bob'],
'Age': [25, 30]
})
# Collect new rows
new_data = [
{'Name': 'Charlie', 'Age': 35},
{'Name': 'Diana', 'Age': 28}
]
new_rows_df = pd.DataFrame(new_data)
# Place new rows before existing data in a single concat
result = pd.concat([new_rows_df, existing_df], ignore_index=True)
print(result)
Output:
Name Age
0 Charlie 35
1 Diana 28
2 Alice 25
3 Bob 30
Quick Reference
| Goal | Method |
|---|---|
| Add single row to top | pd.concat([new_row_df, df], ignore_index=True) |
| Add multiple rows to top | pd.concat([new_rows_df, df], ignore_index=True) |
| Add row to bottom | pd.concat([df, new_row_df], ignore_index=True) |
| Build from many rows | Collect in a list, then pd.DataFrame(list) once |
- Use
pd.concat([new_row, df], ignore_index=True)to add rows at the top of a DataFrame. - Always set
ignore_index=Trueto maintain clean sequential indexing. - Avoid the removed
.append()method, and never usepd.concat()inside loops. - Instead, collect all your data in a Python list first, then create the DataFrame in a single operation for optimal performance.