Skip to content

Python Pandas: How to Efficiently Add a Row at the Top of a Pandas DataFrame

Learn the correct way to prepend rows to a Pandas DataFrame using pd.concat() with performance best practices, avoid deprecated .append(), and optimize batch operations.

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]
})

new_row = pd.DataFrame([{'Name': 'Charlie', 'Age': 35}])

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

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]})

df.loc[-1] = ['Charlie', 35]

df = df.sort_index().reset_index(drop=True)

print(df)

Output:

      Name  Age
0  Charlie   35
1    Alice   25
2      Bob   30

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

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

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

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]
})

new_data = [
    {'Name': 'Charlie', 'Age': 35},
    {'Name': 'Diana', 'Age': 28}
]
new_rows_df = pd.DataFrame(new_data)

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=True to maintain clean sequential indexing.
  • Avoid the removed .append() method, and never use pd.concat() inside loops.
  • Instead, collect all your data in a Python list first, then create the DataFrame in a single operation for optimal performance.
Navigation