Python Pandas: How to Append a Dictionary as a Row to a Pandas DataFrame
Adding rows from dictionaries is a common operation when collecting data incrementally, processing API responses, or building DataFrames record by record. Since the .append() method was removed in Pandas 2.0, it is important to know the modern alternatives that offer better performance and clearer intent.
In this guide, you will learn how to append single and multiple dictionary rows to a DataFrame, understand how each method handles mismatched columns, and avoid common performance pitfalls.
Using .loc[] for Single Row Addition
The most direct way to add a single dictionary as a new row is to assign it to a new index position using .loc[]:
import pandas as pd
df = pd.DataFrame({'Name': ['Alice', 'Bob'], 'Age': [25, 30]})
# Dictionary to add
new_row = {'Name': 'Charlie', 'Age': 35}
# Add at the next available index
df.loc[len(df)] = new_row
print(df)
Output:
Name Age
0 Alice 25
1 Bob 30
2 Charlie 35
This method modifies the DataFrame in place and is the fastest option for adding a single row.
Behavior with Non-Sequential Indices
When the existing index contains non-sequential values, len(df) still works but may produce an index that does not follow the existing pattern:
import pandas as pd
df = pd.DataFrame({'Name': ['Alice'], 'Age': [25]}, index=[100])
df.loc[len(df)] = {'Name': 'Bob', 'Age': 30}
print(df)
Output:
Name Age
100 Alice 25
1 Bob 30
Using len(df) as the index assigns the new row based on the current number of rows, not the existing index values. If you need a clean sequential index afterward, call df.reset_index(drop=True) to renumber all rows starting from zero.
Using pd.concat() for Multiple Rows
When you need to add several dictionary rows at once, convert them into a DataFrame and concatenate in a single operation:
import pandas as pd
df = pd.DataFrame({'Name': ['Alice'], 'Age': [25]})
# List of dictionaries to add
new_rows = [
{'Name': 'Bob', 'Age': 30},
{'Name': 'Charlie', 'Age': 35}
]
# Convert to DataFrame and concatenate
df = pd.concat([df, pd.DataFrame(new_rows)], ignore_index=True)
print(df)
Output:
Name Age
0 Alice 25
1 Bob 30
2 Charlie 35
The ignore_index=True parameter resets the index so that all rows are numbered sequentially.
Why Batch Processing Matters
A critical mistake is placing pd.concat() inside a loop. Each call copies the entire existing DataFrame, resulting in quadratic time complexity:
import pandas as pd
# Wrong: concatenating inside the loop
df = pd.DataFrame(columns=['A'])
for i in range(1000):
df = pd.concat([df, pd.DataFrame([{'A': i}])]) # Full copy every iteration
print(df)
Output:
A
0 0
0 1
0 2
0 3
0 4
.. ...
0 995
0 996
0 997
0 998
0 999
[1000 rows x 1 columns]
The correct approach collects all dictionaries first and creates the DataFrame in one step:
import pandas as pd
# Correct: collect first, convert once
rows = [{'A': i} for i in range(1000)]
df = pd.DataFrame(rows) # Single operation
Output:
A
0 0
1 1
2 2
3 3
4 4
.. ...
995 995
996 996
997 997
998 998
999 999
[1000 rows x 1 columns]
Both produce the same result, but the second approach is typically 50 to 100 times faster for large datasets.
Always collect your dictionaries in a plain Python list during the loop, then call pd.DataFrame() or pd.concat() once after the loop completes. This changes the operation from O(n squared) to O(n).
Why .append() Was Deprecated
If you encounter older code or tutorials, you may see the .append() method. It was removed in Pandas 2.0 and will raise an error:
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'
The method was deprecated for several reasons:
- Performance: It created a full copy of the DataFrame on every call, making it extremely slow in loops.
- Misleading name: Unlike Python's
list.append(), it did not modify the DataFrame in place. It returned a new DataFrame, which was a frequent source of bugs. - Redundancy:
pd.concat()already provided the same functionality with more flexibility.
Handling Mismatched Columns
When dictionary keys do not perfectly match the DataFrame columns, .loc[] and pd.concat() behave differently. Understanding this distinction helps you choose the right method for your situation.
.loc[] Ignores Extra Keys
Extra dictionary keys that do not correspond to existing columns are silently ignored:
import pandas as pd
df = pd.DataFrame({'Name': ['Alice'], 'Age': [25]})
# Dictionary has an extra 'City' key
df.loc[len(df)] = {'Name': 'Bob', 'Age': 30, 'City': 'NYC'}
print(df)
Output:
Name Age
0 Alice 25
1 Bob 30
The City value is discarded because the column does not exist in the DataFrame.
pd.concat() Creates New Columns
In contrast, pd.concat() adds any new columns from the dictionary, filling missing values with NaN for existing rows:
import pandas as pd
df = pd.DataFrame({'Name': ['Alice'], 'Age': [25]})
new_row = {'Name': 'Bob', 'Age': 30, 'City': 'NYC'}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
print(df)
Output:
Name Age City
0 Alice 25 NaN
1 Bob 30 NYC
Choose .loc[] when you want to enforce the existing column structure and ignore extra fields. Choose pd.concat() when you want the DataFrame to expand dynamically to accommodate new fields.
Practical Example: Data Collection Loop
Here is a realistic pattern for collecting data from a function or API and building a DataFrame efficiently:
import pandas as pd
def fetch_record(record_id):
"""Simulate fetching a record from an external source."""
return {
'Timestamp': pd.Timestamp.now(),
'Value': record_id * 10,
'Status': 'OK' if record_id % 2 == 0 else 'Warning'
}
# Collect all records in a list
collected_rows = []
for i in range(5):
record = fetch_record(i)
collected_rows.append(record)
# Create DataFrame in a single operation
df = pd.DataFrame(collected_rows)
print(df)
Output:
Timestamp Value Status
0 2026-02-16 22:04:03.957029 0 OK
1 2026-02-16 22:04:03.957081 10 Warning
2 2026-02-16 22:04:03.957085 20 OK
3 2026-02-16 22:04:03.957088 30 Warning
4 2026-02-16 22:04:03.957091 40 OK
This pattern keeps the loop lightweight by working only with plain dictionaries, deferring all DataFrame overhead to a single final step.
Quick Reference
| Method | Best For | Modifies Original |
|---|---|---|
df.loc[len(df)] = dict | Single row addition | Yes |
pd.concat([df, pd.DataFrame(rows)]) | Multiple rows at once | No (returns new) |
Collect in list, then pd.DataFrame() | Loops with many iterations | N/A (creates new) |
| Scenario | Recommended Approach |
|---|---|
| Adding one row at a time | df.loc[len(df)] = row_dict |
| Adding several rows at once | pd.concat([df, pd.DataFrame(list_of_dicts)]) |
| Building a DataFrame in a loop | Collect dicts in a list, convert once at the end |
- Use
.loc[len(df)] = dictfor quick, in-place single-row additions. - For multiple rows or loop-based collection, always gather dictionaries in a list first and create the DataFrame in a single operation at the end.
- This approach avoids the performance traps of repeated concatenation and keeps your code both fast and readable.