How do I iterate over rows in a pandas DataFrame, and should I?
Short answer
You can use iterrows or itertuples, but for most work you should not loop at all. Vectorised operations on whole columns are far faster and usually clearer.
The short version
You can loop with iterrows or the faster itertuples, but for most tasks you should not iterate at all. Pandas is built to operate on entire columns at once, so a vectorised expression is both faster and shorter. Keep row loops for genuinely awkward cases only.
The way people reach for first
import pandas as pd
orders = pd.DataFrame({"price": [10, 20, 30], "qty": [1, 2, 3]})
totals = []
for index, row in orders.iterrows():
totals.append(row["price"] * row["qty"])
orders["total"] = totals
This works, but iterrows is slow because it builds a Series for every row, and it loses column dtypes along the way.
The vectorised way
The same result comes from multiplying the two columns directly. Pandas applies the operation element by element in fast C code.
orders["total"] = orders["price"] * orders["qty"]
One line, no list, no dtype surprises, and much faster on large frames.
When you really must loop
If you cannot express the logic as a column operation, prefer itertuples, which yields lightweight named tuples and keeps dtypes.
for row in orders.itertuples():
print(row.price, row.qty)
Access fields by attribute (row.price), not by position, so the loop still reads clearly.
Why this is worth caring about
On a few dozen rows the difference is invisible. On a few million it is the gap between a script that returns instantly and one that runs for minutes. The habit to build is to ask "can I write this as an operation on the whole column?" before writing any loop over a DataFrame.
Modifying rather than reading
The examples above read each row. If your goal is to build a new column from existing ones, do not loop or collect results at all: assign the whole column in one expression, as shown above. Where the logic genuinely needs several columns and cannot be written as a single expression, prefer a dedicated approach for applying a function to a column rather than a hand-rolled row loop, because it keeps the intent clear and avoids the iterrows dtype pitfalls. The rule to carry away is simple: reach for row iteration last, only when neither a whole-column operation nor a column-applied function will do.
Enjoying these?
Go from piecing together answers to real fluency.
Our courses take you from looking things up to doing the work with confidence, step by step on real data.
Browse the courses