Question
How can I create an empty Pandas DataFrame and add complete rows to it one at a time?
For example, I start with:
import pandas as pd
df = pd.DataFrame(columns=["lib", "qty1", "qty2"])
I can create a new row and assign one value with:
df.loc[len(df), "qty1"] = 10.0
However, this sets only one field at a time. What is a better way to add a full row to the DataFrame?
Short Answer
You will learn how to add complete rows to a Pandas DataFrame, how .loc and pd.concat() work for this task, and why collecting rows first is usually the fastest design.
Concept
A Pandas DataFrame is a two-dimensional table: rows represent records and columns represent fields of each record.
To add a complete row, provide values for every relevant column. For a small, incremental workflow, assign a list or dictionary with .loc:
df.loc[len(df)] = ["A", 10.0, 20.0]
However, repeatedly growing a DataFrame inside a loop is inefficient. DataFrames are designed primarily for analyzing already-collected tabular data. Each expansion may require Pandas to create a larger internal structure and copy existing data.
When many rows are involved, store each row in a normal Python list first, then create one DataFrame at the end. This is both clearer and usually much faster.
Mental Model
Think of a DataFrame as a printed spreadsheet. Adding a line to a small sheet is easy, but repeatedly reprinting the entire sheet every time you add a line becomes wasteful.
A Python list is like a temporary notebook where you quickly write down incoming records. Once you have finished collecting them, you turn the notebook into one clean spreadsheet by creating a DataFrame once.
Syntax and Examples
For a small number of rows, use .loc with the next index.
import pandas as pd
df = pd.DataFrame(columns=["lib", "qty1", "qty2"])
df.loc[len(df)] = ["lib_a", 10.0, 5.0]
df.loc[len(df)] = ["lib_b", 7.5, 12.0]
print(df)
Output:
lib qty1 qty2
0 lib_a 10.0 5.0
1 lib_b 7.5 12.0
The list on the right must follow the DataFrame column order.
A dictionary is often safer because each value is explicitly named:
df.loc[len(df)] = {
"lib": "lib_c",
"qty1": 3.0,
"qty2": 8.0,
}
For a batch of rows, use pd.concat():
new_rows = pd.DataFrame([
{"lib": "lib_d", "qty1": , : },
{: , : , : },
])
df = pd.concat([df, new_rows], ignore_index=)
Step by Step Execution
Consider this code:
import pandas as pd
df = pd.DataFrame(columns=["lib", "qty1", "qty2"])
row = {"lib": "central", "qty1": 10.0, "qty2": 4.0}
df.loc[len(df)] = row
Step by step:
pd.DataFrame(columns=[...])creates a DataFrame with three named columns and no rows.rowis a dictionary containing values for one record.len(df)is0because the DataFrame is empty.df.loc[0]selects row index0. Since it does not exist yet, Pandas creates it.- Pandas matches dictionary keys such as
"qty1"to DataFrame column names and stores the values.
After the assignment, df contains one row:
lib qty1 qty2
0 central 10.0 4.0
Real World Use Cases
Adding rows is useful when a program receives records gradually, such as:
- Reading a small number of form submissions before displaying them in an admin report.
- Recording results from a short API polling job.
- Building a table of validation errors while processing an upload.
- Combining summary records created by separate functions.
- Adding manually entered adjustments to a small inventory table.
For large CSV files, databases, event streams, or API responses with many records, collect or load data in batches rather than extending a DataFrame one row at a time.
Real Codebase Usage
In production code, developers commonly avoid mutation of a DataFrame in a long loop. Instead, they use one of these patterns:
Collect dictionaries, then build once
records = []
for item in api_items:
records.append({
"lib": item["name"],
"qty1": item["available"],
"qty2": item["reserved"],
})
df = pd.DataFrame(records, columns=["lib", "qty1", "qty2"])
This is the preferred approach when the final number of rows is unknown.
Validate before collecting
records = []
for item in api_items:
if "name" not in item:
continue
records.append({"lib": item["name"], "qty1": 0, "qty2": 0})
The early continue acts as a guard clause: invalid input is skipped before it can create a malformed row.
Combine already-built tables
df = pd.concat([january_df, february_df], ignore_index=)
Common Mistakes
Using DataFrame.append() in new code
# Do not use in modern Pandas
# df = df.append(new_row, ignore_index=True)
DataFrame.append() was deprecated in Pandas 1.4 and removed in Pandas 2.0. Use pd.concat() or collect records in a list.
Repeatedly concatenating inside a large loop
# Works, but can become slow for many rows
for row in rows:
df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)
Avoid this when processing many records. Build records first and call pd.DataFrame(records) once.
Supplying list values in the wrong order
df.loc[len(df)] = [10.0, 5.0, "lib_a"] # Wrong order
Pandas assigns list values by position, not by meaning. Use the exact column order or use a dictionary.
Creating accidental new columns
df.loc[(df), ] =
Comparisons
| Approach | Best for | Main consideration |
|---|---|---|
df.loc[len(df)] = row | A few rows during interactive work | Simple, but inefficient for large loops |
pd.concat([df, new_df], ignore_index=True) | Combining DataFrames or batches | Create new_df with one or more rows first |
records.append(row) then pd.DataFrame(records) | Many incoming rows | Usually the preferred and most efficient pattern |
DataFrame.append() | None in modern code | Deprecated and removed; do not use it |
Lists and dictionaries also differ when representing a row:
| Row format |
|---|
Cheat Sheet
import pandas as pd
columns = ["lib", "qty1", "qty2"]
df = pd.DataFrame(columns=columns)
Add one small row:
df.loc[len(df)] = ["lib_a", 10.0, 5.0]
Add one named row:
df.loc[len(df)] = {"lib": "lib_a", "qty1": 10.0, "qty2": 5.0}
Add a batch:
new_df = pd.DataFrame(records)
df = pd.concat([df, new_df], ignore_index=True)
Preferred for many rows:
records = []
records.append({"lib": "lib_a", "qty1": 10.0, "qty2": 5.0})
df = pd.DataFrame(records, columns=["lib", "qty1", "qty2"])
Rules:
FAQ
How do I add a row to a Pandas DataFrame?
For a small number of rows, assign it with .loc, such as df.loc[len(df)] = {"name": "Ana", "score": 95}.
What is the best way to add many rows in Pandas?
Append dictionaries or lists to a Python list, then call pd.DataFrame(records) once after the loop.
Why is adding rows one at a time slow in Pandas?
A DataFrame may need to allocate and copy data when it grows. Repeating that work for every row is more expensive than building the table once.
Should I use DataFrame.append()?
No. It was deprecated in Pandas 1.4 and removed in Pandas 2.0. Use pd.concat() or a list of records instead.
Does df.loc[len(df)] always add a new row?
It adds or replaces the row whose label equals len(df). It is reliable for a simple default sequential index, but is less suitable after custom indexing or row deletion.
Can a dictionary omit a DataFrame column when adding a row?
Yes. Columns not supplied generally receive missing values such as NaN. Include all required fields if the row must be complete.
Mini Project
Description
Create a small inventory report from records that arrive one at a time. The project demonstrates the recommended pattern: collect named records in a Python list, then create a DataFrame once and calculate a useful result.
Goal
Build an inventory DataFrame and identify items whose available quantity is below their reorder level.
Requirements
Store each inventory item as a dictionary with item, available, and reorder_level fields.
Collect at least four item records in a Python list.
Create one Pandas DataFrame after collecting the records.
Add a needs_reorder column that compares available stock with the reorder level.
Print only the items that need to be reordered.
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Call a Function by Name in a Python Module
Learn how to call a function by name in a Python module using strings, getattr, and safe patterns for dynamic function dispatch.
Catch Multiple Exceptions in One except Block in Python
Learn how to catch multiple exceptions in one Python except block using tuples, with examples, mistakes, and real-world usage.