Module 14 · Core

Map, apply, and vectorize

Choose an appropriate way to transform values and create categories.

Before you start: You can create columns, filter values, and define a short Python function.

Python loads from this site on your first run.

Each run starts with fresh data. Download any exported files before another run. Your written answers and code drafts are saved on this device; export your work to keep a backup.

M14C01 · Explore · predict · practice

Map codes to meaningful labels

Series.map() can look up each value in a dictionary. A code missing from the dictionary produces a missing result. Keep the source codes so you can investigate labels that were not mapped.

Read the example

codes = pd.Series(["AI", "IT", "CS", None], dtype="string")
labels = {"AI": "Artificial Intelligence", "IT": "Information Technology"}
result = pd.DataFrame({"Code": codes, "Label": codes.map(labels)})
print(result)
Verified example output · pandas 2.2.3
   Code                    Label
0    AI  Artificial Intelligence
1    IT   Information Technology
2    CS                      NaN
3  <NA>                      NaN

Watch for this. The meaning of a code must come from a stated codebook, not a guess.

Your turn

For this exercise, define CS as Cybersecurity. Add it to the mapping and rebuild the result while leaving the original missing code missing.

For the code check, keep these result variable names: result. Different valid approaches are welcome.

Show a hint

Add one dictionary key-value pair; do not fill every missing label.

Show one worked solution
codes = pd.Series(["AI", "IT", "CS", None], dtype="string")
labels = {"AI": "Artificial Intelligence", "IT": "Information Technology", "CS": "Cybersecurity"}
result = pd.DataFrame({"Code": codes, "Label": codes.map(labels)})
print(result)

The CS row now has its specified label; the row without a source code remains missing.

Compare your explanations
  1. Read. The codes we want to look up.
  2. Predict. The entry for CS, which is not mapped, and the entry whose source code is missing.
  3. Explain. One is an observed but unrecognized code; the other was never recorded.

One quick check

What happens to an unmapped key in Series.map with a plain dictionary?

What you should be able to explain. Check what a lookup leaves unmatched.

M14C02 · Explore · predict · practice

Apply a function to each value

A Python function can express a custom rule for one value. Series.apply() calls that function for each entry. When the same rule can be written with built-in column operations, those operations are often simpler and more efficient.

Read the example

scores = pd.Series([58, 79, 80, 95])
def status(score):
    return "Secure" if score >= 80 else "Review"
result = scores.apply(status)
print(result.tolist())
Verified example output · pandas 2.2.3
['Review', 'Review', 'Secure', 'Secure']

Watch for this. These labels use an exercise rule, not an official grading policy.

Your turn

Create the same labels without a custom function. Use a Boolean comparison and a mapping from True/False to labels.

For the code check, keep these result variable names: result. Different valid approaches are welcome.

Show a hint

Start with scores.ge(80).

Show one worked solution
scores = pd.Series([58, 79, 80, 95])
result = scores.ge(80).map({True: "Secure", False: "Review"})
print(result.tolist())

The labels are unchanged. The column comparison makes the threshold visible without a separate function.

Compare your explanations
  1. Read. One score value, not the whole Series.
  2. Predict. Secure, because the condition uses >=.
  3. Explain. No. Built-in arithmetic, comparisons, string methods, and aggregations often do the job directly.

One quick check

What does ge(80) mean?

What you should be able to explain. Use a custom function when it adds clarity, not by default.

M14C03 · Explore · predict · practice

A row function versus column arithmetic

DataFrame.apply(..., axis=1) passes one row at a time to a function. Here Quiz is out of 20 and Project is out of 50, so both must be scaled before weighting them. A direct column formula can express the same calculation.

Read the example

df = pd.DataFrame({"Quiz": [16, 12, 18], "Project": [40, 35, 45]})
result = df.apply(lambda row: (row["Quiz"] / 20) * 40 + (row["Project"] / 50) * 60, axis=1)
print(result.round(2).tolist())
Verified example output · pandas 2.2.3
[80.0, 66.0, 90.0]

Watch for this. Check the maximum points and weights before calculating a percentage.

Your turn

Compute the same weighted percentage with direct column arithmetic, without apply or lambda.

For the code check, keep these result variable names: df, result. Different valid approaches are welcome.

Show a hint

Perform each calculation on the entire df["Quiz"] or df["Project"] Series.

Show one worked solution
df = pd.DataFrame({"Quiz": [16, 12, 18], "Project": [40, 35, 45]})
result = (df["Quiz"] / 20) * 40 + (df["Project"] / 50) * 60
print(result.round(2).tolist())

The percentages remain 80.0, 66.0, and 90.0. Direct arithmetic also preserves the row index.

Compare your explanations
  1. Read. The function needs Quiz and Project from the same row.
  2. Predict. 80.0: 16/20 contributes 32 and 40/50 contributes 48.
  3. Explain. It would sum raw points without the stated 40% and 60% weights.

One quick check

With DataFrame.apply, axis=1 passes which object to the function?

What you should be able to explain. Prefer direct column formulas for ordinary arithmetic.

M14C04 · Explore · predict · practice

Fixed intervals versus quantile groups

cut() groups numbers using value boundaries that you choose. qcut() uses sample quantiles to form groups with roughly similar counts. They answer different questions, and equal-count groups need not have equal numeric widths.

Read the example

values = pd.Series([10, 20, 30, 100])
fixed = pd.cut(values, bins=[0, 50, 100], labels=["Lower", "Upper"], include_lowest=True)
quantile = pd.qcut(values, q=2, labels=["Lower_half", "Upper_half"])
result = pd.DataFrame({"Value": values, "Fixed_band": fixed, "Quantile_band": quantile})
print(result)
Verified example output · pandas 2.2.3
   Value Fixed_band Quantile_band
0     10      Lower    Lower_half
1     20      Lower    Lower_half
2     30      Lower    Upper_half
3    100      Upper    Upper_half

Watch for this. Tied values can prevent qcut from making the requested number of distinct bins; inspect boundaries and group counts.

Your turn

Use students(). Create Score_band using boundaries 0, 60, 80, 100 with labels Developing, Progressing, Secure. Include both 0 and 100, with 60 in Developing and 80 in Progressing. Store the count of each band in a Series named result.

For the code check, keep these result variable names: result. Different valid approaches are welcome.

Show a hint

Use cut with right=True and include_lowest=True, then value_counts(sort=False).

Show one worked solution
df = students()
df["Score_band"] = pd.cut(df["Score"], bins=[0, 60, 80, 100], labels=["Developing", "Progressing", "Secure"], right=True, include_lowest=True)
result = df["Score_band"].value_counts(sort=False)
print(result)

The counts are 1 Developing, 5 Progressing, and 6 Secure. These are practice labels with explicit boundary rules.

Compare your explanations
  1. Read. cut().
  2. Predict. Three values are in Lower; two are in Lower_half.
  3. Explain. Their boundaries depend on the current sample rather than a fixed scoring rule.

One quick check

Which method is appropriate for fixed score boundaries?

What you should be able to explain. Choose bands by meaning or by sample distribution, as required.

Further detail: official documentation for this topic.