Skip to main content

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
note

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]
note

Both produce the same result, but the second approach is typically 50 to 100 times faster for large datasets.

tip

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
info

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

MethodBest ForModifies Original
df.loc[len(df)] = dictSingle row additionYes
pd.concat([df, pd.DataFrame(rows)])Multiple rows at onceNo (returns new)
Collect in list, then pd.DataFrame()Loops with many iterationsN/A (creates new)
ScenarioRecommended Approach
Adding one row at a timedf.loc[len(df)] = row_dict
Adding several rows at oncepd.concat([df, pd.DataFrame(list_of_dicts)])
Building a DataFrame in a loopCollect dicts in a list, convert once at the end
  • Use .loc[len(df)] = dict for 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.