Pandas apply vs map vs applymap
Use Series.map for element-wise transforms on one column, DataFrame.apply for row-wise or column-wise functions, and DataFrame.map (called applymap before pandas 2.1) for element-wise transforms on a whole DataFrame. If a built-in vectorized method exists, use that instead of any of them.
When Should You Use Series.map?
map on a Series transforms each element one at a time and also accepts a dict or Series for lookup-style replacement. It is the go-to for recoding a single column.
import pandas as pd
s = pd.Series(["NY", "CA", "NY", "TX"])
s.map({"NY": "New York", "CA": "California"})
# 0 New York
# 1 California
# 2 New York
# 3 NaN <- unmapped values become NaN
s.map(len) # element-wise function: 2, 2, 2, 2
What Does the axis Parameter Do in DataFrame.apply?
DataFrame.apply passes whole rows or whole columns to your function: axis=0 (default) sends each column as a Series, axis=1 sends each row. Use it when the logic needs several values at once.
df = pd.DataFrame({"price": [10, 20], "qty": [3, 5]})
df.apply(lambda col: col.max(), axis=0) # per column: price 20, qty 5
df.apply(lambda row: row["price"] * row["qty"], axis=1)
# 0 30
# 1 100
Remember: axis=1 means "the function consumes a row," not "operate on columns."
What Happened to applymap in Pandas 2.1?
Pandas 2.1 renamed DataFrame.applymap to DataFrame.map for consistency with Series; the old name is deprecated and emits a FutureWarning. Both do the same thing โ apply a function to every individual cell.
df = pd.DataFrame({"a": [1.234, 5.678], "b": [9.876, 3.21]})
df.map(lambda x: round(x, 1)) # pandas >= 2.1
df.applymap(lambda x: round(x, 1)) # older pandas; deprecated now
When Should You Vectorize Instead?
All three run a Python function per element or per row, which is often 10โ100x slower than pandas' built-in vectorized operations. Reach for column arithmetic, .str, .dt, and np.where before writing a lambda.
# Slow:
df.apply(lambda row: row["price"] * row["qty"], axis=1)
# Fast โ same result:
df["price"] * df["qty"]
See vectorization in pandas for the full speed comparison, and lambda functions if the one-liner syntax is new to you.
Common Pitfalls
- map with a dict returns NaN for missing keys. Use
s.replace({...})if you want unmapped values kept as-is. - axis confusion:
apply(f, axis=1)receives rows. If your function indexesrow["col"], you needaxis=1. - Row-wise apply is slow: each row becomes a temporary Series. On a million rows this dominates runtime.
- Series.apply vs Series.map: nearly interchangeable for functions, but only
maptakes a dict/Series lookup.
Pro Tip: If you must apply a Python function to a column, s.map(f) beats df.apply with axis=1 โ pulling one column out first avoids materializing every row as a Series.