New columns and rename

Create columns, cut into bins, rename, drop, and np.where.

Assign a new name on the left. The right-hand side can use other columns, NumPy, or pd.cut. Prefer df["col"] = ... or df.assign(...) over inplace puzzles.

Goal

Add computed columns, bin numbers, rename headings, and drop what you no longer need.

Arithmetic and flags

df = pd.DataFrame(
    {
        "name": ["Ada", "Alan", "Grace", "Linus"],
        "score": [98, 91, 95, 88],
        "team": ["A", "B", "A", "B"],
    }
)
df["passed"] = df["score"] >= 90
df["points_from_100"] = 100 - df["score"]
print(df)

assign returns a new frame

Useful in a pipeline. Original df is unchanged unless you assign back.

df = pd.DataFrame(
    {
        "name": ["Ada", "Alan", "Grace", "Linus"],
        "score": [98, 91, 95, 88],
        "team": ["A", "B", "A", "B"],
    }
)
out = df.assign(
    passed=df["score"] >= 90,
    band=lambda x: np.where(x["score"] >= 95, "high", "mid"),
)
print(out)
print()
print("original columns:", list(df.columns))

Bins with cut and qcut

cut uses edges you choose. qcut uses quantiles (roughly equal counts).

df = pd.DataFrame(
    {
        "name": ["Ada", "Alan", "Grace", "Linus"],
        "score": [98, 91, 95, 88],
    }
)
df["grade"] = pd.cut(
    df["score"],
    bins=[0, 89, 94, 100],
    labels=["C", "B", "A"],
    include_lowest=True,
)
df["quartile"] = pd.qcut(df["score"], q=2, labels=["lower", "upper"])
print(df)
print()
print(df["grade"].value_counts())

np.where for if/else columns

df = pd.DataFrame(
    {
        "name": ["Ada", "Alan", "Grace", "Linus"],
        "score": [98, 91, 95, 88],
    }
)
df["status"] = np.where(df["score"] >= 90, "pass", "retry")
print(df)

Nested np.where works; more than two branches is often cleaner as cut.

Rename and drop

df = pd.DataFrame(
    {
        "name": ["Ada", "Alan"],
        "score": [98, 91],
        "extra": [1, 2],
    }
)
renamed = df.rename(columns={"name": "student", "score": "marks"})
print(renamed)
print()
print(renamed.drop(columns=["extra"], errors="ignore"))

errors="ignore" skips names that are not there.

Insert at a position

df = pd.DataFrame({"name": ["Ada", "Alan"], "score": [98, 91]})
df.insert(1, "year", 2024)
print(df)
Tip

Vectorized column math (df["units"] * df["price"]) is faster and clearer than apply for row-wise arithmetic. Save apply for logic that cannot be expressed with columns.