Python · medium
Explain the difference between .apply(), .map(), and vectorized ops in pandas.
Asked in Data Scientist interviews, in the Python round.
Short answer
Vectorized ops are fastest, map is element-wise on a Series, and apply is flexible but slower (row or column).
| .map() | .apply() | Vectorized | |
|---|---|---|---|
| Works on | A Series | A Series or DataFrame | Whole arrays |
| Loops in Python | Yes | Yes | No, runs in C |
| Relative speed | Slow | Slow | 10 to 100 times faster |
| Typical use | Value lookup or dict swap | Row-wise logic you cannot vectorize | Arithmetic, comparisons, str and dt methods |
| Reach for it when | Mapping a small set of values | Nothing else expresses it | Always, first |
How to answer it
Definition
All three transform a column. The difference is where the loop runs, and therefore how fast it is. The table above compares them directly.
Example
# vectorized: the whole array at once, in C
df["total"] = df["price"] * df["qty"]
df["is_big"] = df["total"] > 100
df["domain"] = df["email"].str.split("@").str[1]
# map: element-wise on a Series, good for a lookup
df["region"] = df["country"].map({"GB": "EMEA", "US": "AMER"})
# apply: a Python function per row, the slow last resort
df["score"] = df.apply(lambda r: custom(r["a"], r["b"]), axis=1)
Output
%timeit df["price"] * df["qty"] # 1.2 ms
%timeit df.apply(lambda r: r["price"] * r["qty"], axis=1) # 890.0 ms
Roughly 700 times slower on a million rows, for identical output. That gap is the entire reason the question is asked.
Why vectorized is faster
A vectorized operation hands the whole array to compiled code and loops there. .apply(axis=1) builds a Python Series for every row, calls a Python function, and collects the results. The work is the same; the per-row Python overhead is not.
Key points
- Reach for vectorized first: arithmetic, comparisons,
.str,.dt,np.where,np.select. .map()is for a Series and shines with a dict lookup. It is still a Python-level loop..apply(axis=1)is the slowest and the most flexible. Use it when nothing else expresses the logic.- Conditional logic is usually
np.wherefor two branches andnp.selectfor several, notapply. .iterrows()is slower still, and almost never belongs in production code.
FAQ
- Is
.applyon a Series also slow? Yes, though less so thanaxis=1, which materialises a row object each time. - What if my function calls an API? Then the network dominates and
applyis fine. Saying that shows judgement rather than dogma. - Does
.applymapmatter? It is element-wise across a whole DataFrame and shares the same performance profile.
The 30-second answer
They all transform a column, and the difference is where the loop runs. Vectorized operations push the whole array into compiled code, so arithmetic, comparisons and the string and datetime accessors are fastest by a wide margin. map is element-wise on a Series and is what I use for a dictionary lookup. apply with axis one is the flexible one and the slowest, because it builds a Python object per row.
In practice I try to express it vectorized first, use np.where or np.select for conditional logic, and only fall back to apply when the logic genuinely cannot be written over arrays. On a million rows the difference is a couple of milliseconds against most of a second.
In the interview
- What they are checking: that you know
apply(axis=1)is a Python loop in disguise. - Common mistake: using
applyfor arithmetic because it "reads clearer", then wondering why the pipeline takes ten minutes.