Written and maintained by CASRAI Editorial Board
Published
On this page
pandas map usually means Series.map(), the pandas method that replaces each value in a column using a dictionary, a function or another Series. Since pandas 2.1.0 the same idea exists for a whole table as DataFrame.map(). This guide is for researchers and analysts recoding survey, lab or registry data in Python, and it ends with the failure modes that silently produce missing values.
What pandas map is used for in research
Series.map() is an element-wise lookup: every value in a column goes in, one value per input comes out, and the result is a new Series with the same index as the original. In research data work the typical jobs are recoding numeric survey codes into labels, translating site or instrument identifiers into names, and applying a small cleaning function to one text column. Whole-table, element-wise work uses DataFrame.map().
pandas map versus apply, replace and applymap
| Method | Operates on | Use it when |
|---|---|---|
Series.map(func, na_action=None) |
One column, value by value | You have a dict, Series or one-value-in, one-value-out function |
DataFrame.map(func, na_action=None) |
Every cell of a DataFrame | The same single-value function applies to the whole table (added in pandas 2.1.0) |
DataFrame.applymap |
Every cell | Older name for DataFrame.map; migrate df.applymap(f) to df.map(f) |
DataFrame.apply |
Whole rows or columns | The function needs a full row or column; see pandas apply |
DataFrame.replace |
Matching values | Only some values change and the rest should stay as they are |
The pandas documentation lists DataFrame.apply, DataFrame.replace and Series.map as the related methods for DataFrame.map.
Install or access
pandas is installed from the official project: see the installation page at pandas.pydata.org. Both methods are part of the core library, so no extra package is needed. Check your version with import pandas as pd; pd.__version__, because DataFrame.map needs pandas 2.1.0 or later, and the engine argument and **kwargs on Series.map were added in 3.0.0.
Step-by-step: recode survey codes with Series.map
Signature in the pandas 3.0 documentation: Series.map(func=None, na_action=None, engine=None, **kwargs). The first parameter is func in current docs (older documentation calls it arg), and it accepts a function, a dict or a Series.
- Import pandas and load the column. A small illustrative example:
import numpy as np; import pandas as pd; s = pd.Series([1, 2, 3, 9, np.nan]), where 1 to 3 are valid Likert codes, 9 is an invalid code and the last value is missing. - Define the lookup:
labels = {1: 'Strongly disagree', 2: 'Disagree', 3: 'Neutral'}. - Apply it:
s.map(labels). - Assign the result to a new column so the raw codes stay available for audit:
df['q1_label'] = df['q1'].map(labels). - Count the unmapped values before moving on: compare
df['q1_label'].isna().sum()withdf['q1'].isna().sum(). Any difference is a code missing from your dictionary.
Map with a function
Pass any callable that takes one value and returns one value, for example s.map('I am a {}'.format). DataFrame.map works the same way across all cells: df.map(lambda x: len(str(x))). Extra keyword arguments can be passed through, as in df.map(round, ndigits=1) from the pandas documentation example.
Output and how to read it
Run in pandas 3.0.5, the Likert example returns ['Strongly disagree', 'Disagree', 'Neutral', nan, nan]. The code 9 and the original missing value both become NaN, which is the documented behaviour: with a dict, values that are not found as keys become NaN. Because the two cases look identical in the output, compare the result against the source column rather than trusting the label column alone.
If the dictionary is a subclass that defines __missing__, such as collections.defaultdict, the default is used instead. With defaultdict(lambda: 'Other', labels) the same input returns ['Strongly disagree', 'Disagree', 'Neutral', 'Other', 'Other'], which also labels the missing value ‘Other’. Decide whether that is what the analysis plan intends.
The na_action argument
na_action accepts None (default) or 'ignore'. With 'ignore', NaN is propagated without being passed to the mapping. This matters for functions: pd.Series(['cat', np.nan]).map('I am a {}'.format) returns 'I am a nan' for the missing value, a string that no longer counts as missing. Adding na_action='ignore' keeps it as NaN.
Troubleshooting
Unexpected NaN after mapping
Some value was not a key in the dictionary. Check for type mismatches (the string '1' does not match the integer 1), stray whitespace in text codes, and codes you did not anticipate. List them with df.loc[df['q1_label'].isna() & df['q1'].notna(), 'q1'].unique().
Missing values turned into the text ‘nan’
A function mapped without na_action='ignore' received NaN. Add the argument or handle missing values inside the function.
Categorical columns
For a categorical Series, the pandas documentation states that the function is applied only to the categories, so it is called once per unique category rather than once per row.
Engine error
The engine argument (new in pandas 3.0.0) is only for functions decorated with a supported JIT such as numba.jit; passing it with a dict or Series raises an error.
Slow code on large tables
The pandas documentation advises that calling a Python function on each element is slow and that vectorised operations are preferable, for example squaring a frame with df ** 2 instead of mapping a lambda. Use map for lookups and genuinely element-wise logic, and vectorised operators for arithmetic. To keep only rows matching a set of values, see pandas isin; to drop rows with missing values after mapping, see pandas dropna.
Free alternatives
| Task | Alternative in pandas |
|---|---|
| Change only some values, leave the rest | Series.replace or DataFrame.replace |
| Operate on whole rows or columns | DataFrame.apply (guide) |
| Element-wise arithmetic | Vectorised operators such as df ** 2 |
Older code using applymap |
DataFrame.map |
If you are building the table first, see how to create a DataFrame in Python.
FAQ
What does pandas map do?
It substitutes each value of a Series using a dict, a Series or a function and returns a Series with the same index.
Is it pandas.map or Series.map?
There is no top-level pd.map. Call Series.map on a column or DataFrame.map on a table.
What replaced applymap?
DataFrame.map, added in pandas 2.1.0. Replace df.applymap(f) with df.map(f).
What happens to values missing from my dictionary?
They become NaN, unless the dict is a subclass with __missing__ such as defaultdict.
Sources
- pandas documentation: pandas.Series.map (pandas 3.0 docs, read 2026-11-08).
- pandas documentation: pandas.DataFrame.map (read 2026-11-08).
- Example outputs above were run in pandas 3.0.5.







