pandas Cheatsheet

Modifying Data

Use this pandas reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Adding and Replacing Columns

# Simple assignment
df["new_col"] = 0
df["full_name"] = df["first"] + " " + df["last"]
df["score_pct"] = df["score"] / df["score"].max() * 100

# .assign() — returns new DataFrame, chainable
df = df.assign(new_col=0)
df = df.assign(
    tax=df["price"] * 0.08,
    total=lambda d: d["price"] + d["tax"],   # use lambda to reference just-added cols
)

# Insert at specific position
df.insert(2, "new_col", df["a"] + df["b"])   # position, name, values

Renaming

# By mapping
df.rename(columns={"old": "new", "a": "b"})
df.rename(index={0: "row_a", 1: "row_b"})

# With a function
df.rename(columns=str.upper)
df.rename(columns=lambda c: c.strip().lower().replace(" ", "_"))

# Rename all columns at once (must provide all)
df.columns = ["id", "name", "score"]

# Rename index
df.index.name = "record_id"

Dropping Columns and Rows

# Columns
df.drop(columns=["col1", "col2"])
df.drop("col1", axis=1)

# Rows by label
df.drop(index=[0, 2, 5])
df.drop("row_a")

# Rows by label range (loc-style)
df.drop(df.index[2:5])

# In-place
df.drop(columns=["tmp"], inplace=True)

Modifying Values — .loc / .at

# Safest way to modify values — avoids SettingWithCopyWarning
df.loc[df["status"] == "active", "score"] += 10
df.loc[0, "name"] = "Alice"
df.at[3, "score"] = 99
df.iat[0, 1] = 42

.apply() — Row-wise or Column-wise

# Along columns (axis=0, default) — function receives a column Series
df.apply(lambda col: col.max() - col.min())

# Along rows (axis=1) — function receives a row Series
df.apply(lambda row: row["a"] + row["b"], axis=1)

# Apply to a single column
df["score"].apply(lambda x: x * 1.1)
df["name"].apply(str.upper)

# Return multiple values → expands to columns
df[["sin", "cos"]] = df["angle"].apply(
    lambda x: pd.Series({"sin": np.sin(x), "cos": np.cos(x)})
)

Performance note: .apply() with axis=1 is slow — prefer vectorized operations (df["a"] + df["b"]), np.where, or df.eval() when possible.

np.where and pd.cut — Vectorized Conditionals

import numpy as np

# np.where(condition, value_if_true, value_if_false)
df["label"] = np.where(df["score"] >= 60, "pass", "fail")

# Nested np.where
df["grade"] = np.where(df["score"] >= 90, "A",
              np.where(df["score"] >= 80, "B",
              np.where(df["score"] >= 70, "C", "F")))

# pd.cut — bin continuous values into categories
df["bracket"] = pd.cut(df["age"], bins=[0, 18, 35, 65, 100],
                        labels=["child", "young", "adult", "senior"])

# pd.qcut — quantile-based bins
df["quartile"] = pd.qcut(df["score"], q=4, labels=["Q1", "Q2", "Q3", "Q4"])

.map() — Element-wise Mapping (Series)

# Via dict
df["city_code"] = df["city"].map({"NYC": 1, "LA": 2, "Chicago": 3})

# Via function
df["upper"] = df["name"].map(str.upper)

# map() does NOT call the function with missing keys (→ NaN)
# Use fillna() to handle misses
df["code"] = df["city"].map(mapping).fillna(-1)

.replace() — Value Substitution

# Scalar replace
df["col"].replace("old", "new")
df.replace(np.nan, 0)

# Dict of replacements
df["status"].replace({"active": 1, "inactive": 0})
df.replace({"status": {"active": 1}, "grade": {"A": 4.0}})

# List: replace each element with corresponding replacement
df["col"].replace(["bad", "worse"], ["ok", "ok"])

# Regex replace
df["text"].replace(r"\s+", " ", regex=True)

# replace() vs map(): replace keeps unmapped values; map() turns them to NaN

Sorting

# Sort by values
df.sort_values("col")
df.sort_values("col", ascending=False)
df.sort_values(["col1", "col2"], ascending=[True, False])
df.sort_values("col", na_position="first")   # "first" | "last" (default)
df.sort_values("col", key=lambda s: s.str.lower())  # custom key

# Sort by index
df.sort_index()
df.sort_index(ascending=False)
df.sort_index(level="month")   # MultiIndex: sort by one level

# Stable sort (default since pandas 1.x uses mergesort for stability)
df.sort_values("col", kind="stable")

Casting / Type Conversion

df["age"] = df["age"].astype(int)
df["price"] = df["price"].astype(float)
df["flag"] = df["flag"].astype(bool)
df["cat"] = df["cat"].astype("category")
df["text"] = df["text"].astype("string")           # nullable StringDtype
df["date"] = pd.to_datetime(df["date"])
df["n"] = pd.to_numeric(df["n"], errors="coerce")  # invalid → NaN
df = df.astype({"a": int, "b": float})             # multiple at once

# Downcast numeric types to save memory
df["n"] = pd.to_numeric(df["n"], downcast="integer")  # "integer" | "float" | "signed" | "unsigned"

Clipping Values

df["score"].clip(lower=0, upper=100)
df.clip(lower=0)            # clip all columns
df["val"].clip(lower=df["min_val"], upper=df["max_val"])  # per-row bounds

String Column Modifications

df["name"] = df["name"].str.strip()
df["name"] = df["name"].str.lower()
df["name"] = df["name"].str.upper()
df["name"] = df["name"].str.title()
df["name"] = df["name"].str.replace(r"\s+", "_", regex=True)
df["name"] = df["name"].str.slice(0, 10)   # truncate
df["name"] = df["name"].str.pad(20, side="right", fillchar=" ")

Reordering Columns

# Explicit order
df = df[["id", "name", "score", "date"]]

# Move one column to the front
cols = ["target"] + [c for c in df.columns if c != "target"]
df = df[cols]

# Sort columns alphabetically
df = df.reindex(sorted(df.columns), axis=1)