Back to Blog

pandas .map vs .apply vs .applymap — Pick the Right One

Celest KimCelest Kim

Video: pandas .map vs .apply vs .applymap — Pick the Right One by CelesteAI

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Three methods. Three different jobs. And one of them was removed in pandas 3.0 — which is why your copy-pasted Stack Overflow snippet just threw AttributeError.

This post walks through .map, .apply, and .applymap on a tiny prices DataFrame and shows exactly which method belongs where. It’s the question Python data folks Google before they have a good mental model of pandas, and the answer is short enough to memorize.

The setup

Three tickers, three days, three columns. Small enough to fit in your head.

import pandas as pd

df = pd.DataFrame(
  {
    "AAPL": [185.50, 186.20, 184.10],
    "MSFT": [415.30, 418.70, 414.10],
    "GOOG": [142.40, 143.10, 141.80],
  },
  index=pd.to_datetime(["2025-01-15", "2025-01-16", "2025-01-17"]),
)

1. Series.map — Series only, dict is the killer feature

.map is the one you use most. It’s Series-only. It takes a dict, a Series, or a function — and that dict overload is what makes it special.

tickers = pd.Series(["AAPL", "MSFT", "GOOG"])
sectors = tickers.map({"AAPL": "Tech", "MSFT": "Tech", "GOOG": "Comm"})
# Tech, Tech, Comm

If you’re translating ticker → sector, country code → country name, status code → label, this is the one-liner. No merge, no loop, no for statement. Just a dict and .map.

2. Series.apply — Series too, but only takes a function

lower = tickers.apply(lambda s: s.lower())
# aapl, msft, goog

On a Series, .apply is essentially a slower .map — but it only takes a function (no dict overload). Reach for it when the transformation needs logic that doesn’t fit a dict.

3. DataFrame.apply axis=0 — function gets each column as a Series

This is where .apply shows its real power. On a DataFrame, the axis argument changes everything. The default, axis=0, hands your function each column as a Series.

col_std = df.apply(lambda s: s.std())
# AAPL    1.07
# MSFT    2.39
# GOOG    0.65

Per-column reductions — standard deviation, mean, custom z-scores, anything where you want one number out per column — go here.

4. DataFrame.apply axis=1 — function gets each row as a Series

Flip the axis and your function receives each row as a Series.

row_max = df.apply(lambda r: r.max(), axis=1)
# 2025-01-15    415.3
# 2025-01-16    418.7
# 2025-01-17    414.1

This is the row-wise tool. But it’s also a Python loop under the hood — axis=1 is slow on large DataFrames for the same reason iterrows is slow. When you can replace it with column arithmetic (df["A"] + df["B"]), do that.

5. DataFrame.map — the modern element-wise

formatted = df.map(lambda x: f"${x:,.2f}")
#               AAPL     MSFT     GOOG
# 2025-01-15  $185.50  $415.30  $142.40
# 2025-01-16  $186.20  $418.70  $143.10
# 2025-01-17  $184.10  $414.10  $141.80

Format every cell. Cast every cell. Sanitize every string. Whatever you want to do to every cell of the DataFrame independently, DataFrame.map does it.

This method was added in pandas 2.1 as the replacement for applymap. Same signature, same behavior, new name.

6. applymap — gone in pandas 3.0

If your code does this:

df.applymap(lambda x: round(x, 1))

…on pandas 3.0 you get:

AttributeError: 'DataFrame' object has no attribute 'applymap'

applymap was deprecated in pandas 2.1 and removed in pandas 3.0. The fix is a one-line rename:

df.map(lambda x: round(x, 1))

That’s it. Identical signature, identical behavior.

The decision tree

Input Goal Method
Series Dict lookup .map(dict)
Series Function .map(fn) or .apply(fn)
DataFrame Element-wise .map(fn)
DataFrame Per-column .apply(fn) (axis=0)
DataFrame Per-row .apply(fn, axis=1)
DataFrame Anything vectorizable column arithmetic, not .apply

The last row is the most important. .apply is still a Python loop in disguise — axis=1 especially. When you can rewrite a per-row computation as column arithmetic, the vectorized form runs hundreds of times faster.

Want the deeper dive?

Pandas for Finance Ep 6 builds per-column returns, wealth curves, Sharpe ratios, and drawdowns entirely with vectorized arithmetic — the case where you skip .apply entirely and let pandas push everything into NumPy.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.