Mapping Data
Overview
In the previous lesson, you learned how to select specific rows and columns and assign new data to a DataFrame. Often, you'll need to go further — transforming every value in a column according to some rule or custom function. In this lesson, you'll learn how to use .map(), .apply(), and lambda functions to apply transformations across Series and DataFrames efficiently.
Learning Objective
By the end of this lesson, you will have learned how to:
- Map or apply functions to Series or DataFrame columns.
- Use
.apply(),.map(), and lambda functions for transformations.
Key terms
.map(): A Series method that applies a function or a mapping dictionary to each element of the Series, one value at a time.
.apply(): A flexible method available on both Series and DataFrames that applies a function along an axis (row-by-row or column-by-column).
Lambda function: A small, anonymous (unnamed) function written inline using the lambda keyword. Often used as a quick transformation inside .map() or .apply().
Vectorized operation: An operation applied to an entire column at once (e.g., df["price"] * 1.1) without looping — faster and more readable than using .apply() for simple math.
.applymap() / .map() (DataFrame): In newer versions of Pandas (≥ 2.1), DataFrame.map() applies a function element-by-element to every cell in a DataFrame (previously called .applymap()).
Starter Code
This lesson encourages experimentation. Use the included Colaboratory notebook to run the code as you learn, or read the lesson and then freely experiment with the notebook's code.
Introduction
Real datasets are rarely clean or in the exact format you need. Column values may need to be rescaled, categories renamed, strings standardized, or new features derived from existing ones. Pandas provides several tools for these transformations. Understanding when to use each one — and when a simpler vectorized operation is better — is a key data wrangling skill.
Vectorized Operations (The First Choice)
Before reaching for .map() or .apply(), check whether a simple arithmetic or string operation already does what you need. Vectorized operations are applied to the entire column at once and are much faster:
import pandas as pd
df = pd.DataFrame({
"product": ["Laptop", "Phone", "Tablet", "Monitor"],
"price": [999, 699, 499, 349],
"rating": [4.5, 4.2, 3.8, 4.0]
})
# Add 10% tax to every price
df["price_with_tax"] = df["price"] * 1.10
# Convert rating to percentage
df["rating_pct"] = df["rating"] / 5 * 100
print(df)
Output:
product price rating price_with_tax rating_pct
0 Laptop 999 4.5 1098.90 90.0
1 Phone 699 4.2 768.90 84.0
2 Tablet 499 3.8 548.90 76.0
3 Monitor 349 4.0 383.90 80.0
Use vectorized operations whenever the transformation is a straightforward arithmetic expression or a built-in string/date method.
Using .map() on a Series
.map() transforms each value in a Series individually. It accepts a function, a lambda, or a dictionary (for lookup/replacement).
With a Function
def categorize_price(price):
if price >= 800:
return "Premium"
elif price >= 500:
return "Mid-range"
else:
return "Budget"
df["price_tier"] = df["price"].map(categorize_price)
print(df[["product", "price", "price_tier"]])
Output:
product price price_tier
0 Laptop 999 Premium
1 Phone 699 Mid-range
2 Tablet 499 Budget
3 Monitor 349 Budget
With a Lambda Function
A lambda is a compact, one-line function defined inline. Instead of writing a full def, you write lambda argument: expression:
# Capitalize product names
df["product_upper"] = df["product"].map(lambda x: x.upper())
print(df["product_upper"])
Output:
0 LAPTOP
1 PHONE
2 TABLET
3 MONITOR
Name: product_upper, dtype: object
Lambda functions are ideal for simple, one-off transformations that don't need a full named function.
With a Dictionary (Value Replacement)
Pass a dictionary to replace specific values — any value not in the dictionary becomes NaN:
tier_code = {"Premium": "P", "Mid-range": "M", "Budget": "B"}
df["tier_code"] = df["price_tier"].map(tier_code)
print(df[["product", "price_tier", "tier_code"]])
Output:
product price_tier tier_code
0 Laptop Premium P
1 Phone Mid-range M
2 Tablet Budget B
3 Monitor Budget B
Using .apply() on a Series
.apply() works similarly to .map() on a Series, but it is more flexible and is the standard choice when using .apply() on a DataFrame:
# Apply a function to round prices to the nearest hundred
df["rounded_price"] = df["price"].apply(lambda x: round(x, -2))
print(df[["product", "price", "rounded_price"]])
Output:
product price rounded_price
0 Laptop 999 1000
1 Phone 699 700
2 Tablet 499 500
3 Monitor 349 300
Using .apply() on a DataFrame (Row-Wise)
When you call .apply() on a DataFrame with axis=1, the function receives an entire row as a Series. This is useful when a transformation depends on multiple columns:
df = pd.DataFrame({
"name": ["Alex", "Jordan", "Morgan", "Sam"],
"score": [88, 92, 75, 95],
"bonus": [5, 3, 8, 2]
})
def final_score(row):
"""Add bonus to score, cap at 100."""
return min(row["score"] + row["bonus"], 100)
df["final_score"] = df.apply(final_score, axis=1)
print(df)
Output:
name score bonus final_score
0 Alex 88 5 93
1 Jordan 92 3 95
2 Morgan 75 8 83
3 Sam 95 2 97
Use axis=1 to apply row-by-row. The default axis=0 applies column-by-column (less common).
Row-Wise Lambda with .apply()
df["label"] = df.apply(
lambda row: f"{row['name']}: {row['final_score']}", axis=1
)
print(df["label"])
Output:
0 Alex: 93
1 Jordan: 95
2 Morgan: 83
3 Sam: 97
Name: label, dtype: object
Choosing the Right Tool
| Situation | Recommended tool |
|---|---|
| Simple math or string on one column | Vectorized operation (df["col"] * 2) |
| Transform each value in one column | .map() with a function, lambda, or dict |
| Transform each value in one column (complex logic) | .apply() on a Series |
| Transform based on multiple columns | .apply(func, axis=1) on a DataFrame |
| Replace specific category labels | .map() with a dictionary |
Performance tip: Vectorized operations are fastest.
.map()is faster than.apply()on a Series. Row-wise.apply(axis=1)is the slowest — avoid it on very large datasets by restructuring the logic as vectorized operations where possible.
Putting It Together: A Data Science Example
Here's a realistic pipeline that cleans and engineers features from a raw dataset:
import pandas as pd
df = pd.DataFrame({
"student": [" alice", "BOB ", " charlie", "diana"],
"raw_score": [82, 67, 91, 55],
"attempts": [1, 3, 1, 4]
})
# 1. Clean student names
df["student"] = df["student"].map(lambda x: x.strip().title())
# 2. Apply a penalty for multiple attempts (-5 per extra attempt)
df["adjusted_score"] = df.apply(
lambda row: max(row["raw_score"] - (row["attempts"] - 1) * 5, 0),
axis=1
)
# 3. Assign pass/fail
df["result"] = df["adjusted_score"].map(lambda s: "Pass" if s >= 70 else "Fail")
print(df)
Output:
student raw_score attempts adjusted_score result
0 Alice 82 1 82 Pass
1 Bob 67 3 57 Fail
2 Charlie 91 1 91 Pass
3 Diana 55 4 40 Fail
Conclusion
In this lesson, you learned how to transform data in Pandas using vectorized operations, .map(), and .apply(). You used dictionary mappings for value replacement, lambda functions for compact inline transformations, and row-wise .apply() for logic that spans multiple columns. These tools are at the heart of feature engineering — the process of transforming raw data into meaningful inputs for analysis and machine learning. In the next lesson, you'll learn how to group data by categories and sort it to uncover patterns across segments of your dataset.
Practice
Coding Assessment
Practice the concepts from this lesson using this notebook. After completing the exercises, save your notebook to GitHub and submit the link for grading.