DataFrame
Column Manipulation
Data Analysis
Python Pandas
Conditional Logic

How do I create a new column where the values are selected based on an existing column?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Creating a new column from an existing column is one of the most common pandas tasks. The exact method depends on the rule: a simple two-way condition, a mapping table, or several branching conditions. In most cases, the best answer is a vectorized approach rather than a Python loop over rows.

Use np.where for a Simple Two-Way Rule

If the new column depends on one yes-or-no condition, numpy.where is compact and fast.

python
1import numpy as np
2import pandas as pd
3
4df = pd.DataFrame({
5    "sales": [120, 40, 95, 200]
6})
7
8df["performance"] = np.where(df["sales"] >= 100, "high", "normal")
9print(df)

Output:

text
1   sales performance
20    120        high
31     40      normal
42     95      normal
53    200        high

This is usually the cleanest solution when one condition determines one of two values.

Use np.select for Multiple Conditions

If there are several branching rules, np.select is often clearer than nesting multiple np.where calls.

python
1import numpy as np
2import pandas as pd
3
4df = pd.DataFrame({
5    "score": [95, 76, 61, 42]
6})
7
8conditions = [
9    df["score"] >= 90,
10    df["score"] >= 70,
11    df["score"] >= 50,
12]
13
14choices = ["A", "B", "C"]
15
16df["grade"] = np.select(conditions, choices, default="D")
17print(df)

This pattern scales better when the new column depends on more than one threshold or category branch.

Use map for Direct Value Translation

If each old value maps directly to a new value, a dictionary with Series.map is simpler than conditional logic.

python
1import pandas as pd
2
3df = pd.DataFrame({
4    "status_code": [1, 2, 1, 3]
5})
6
7mapping = {
8    1: "pending",
9    2: "approved",
10    3: "rejected",
11}
12
13df["status"] = df["status_code"].map(mapping)
14print(df)

This is ideal when the transformation is a lookup rather than a rule based on ranges or comparisons.

Use apply Only When the Logic Is Truly Custom

apply is flexible, but it is not the first choice for simple column logic. Use it when the transformation is too custom for vectorized operations.

python
1import pandas as pd
2
3df = pd.DataFrame({
4    "price": [9.99, 49.99, 120.00]
5})
6
7def classify_price(value):
8    if value < 10:
9        return "cheap"
10    if value < 100:
11        return "mid"
12    return "premium"
13
14df["bucket"] = df["price"].apply(classify_price)
15print(df)

This is readable, but it is usually slower than np.where, np.select, or map on large DataFrames.

Assign with .loc When Updating Only Certain Rows

Another common pattern is to set a default value first and then update selected rows with .loc.

python
1import pandas as pd
2
3df = pd.DataFrame({
4    "sales": [120, 40, 95, 200]
5})
6
7df["performance"] = "normal"
8df.loc[df["sales"] >= 100, "performance"] = "high"
9
10print(df)

This is very readable when the logic is mostly "default everything, then override some rows."

Why Vectorization Matters

A loop over DataFrame rows usually looks simple at first, but it is much slower and less idiomatic in pandas. Methods such as np.where, np.select, map, and boolean indexing operate on whole arrays and are typically easier to optimize and maintain.

That does not mean apply is forbidden. It just should not be the default answer for rules that already fit a built-in vectorized pattern.

Common Pitfalls

One common mistake is writing Python loops over rows for a problem that np.where or map could solve in one line. That hurts performance and often makes the code harder to read.

Another mistake is choosing map for range-based conditions. map is for direct value translation, not threshold logic.

Developers also sometimes forget about missing values. If a value is not in the mapping dictionary, map returns NaN unless you handle it explicitly.

Finally, chained assignment can create confusing warnings. If you are updating values conditionally, prefer assigning through .loc on the original DataFrame.

Summary

  • Use np.where for simple two-way conditions.
  • Use np.select for multiple branching conditions.
  • Use Series.map for direct old-value to new-value translation.
  • Use .loc when you want a default value plus targeted overrides.
  • Prefer vectorized pandas patterns over row-by-row loops whenever possible.

Course illustration
Course illustration

All Rights Reserved.