Keep this page nearby

Pandas quick reference

Start with the problem you want to solve. Each recipe uses a tested course example and links to its full explanation.

Use the sidebar search for ordinary language or a method name. Open the glossary for unfamiliar terms.

Build a table from a dictionary

I can turn a dictionary of lists into a DataFrame.

Show the tested example
data = {"Name": ["Amira", "Bilal", "Chloe"], "Age": [20, 21, 19]}
df = pd.DataFrame(data)
print(df)
print(type(df).__name__)
print("Rows and columns:", df.shape)

Watch for this: Call pd.DataFrame(...) with a capital D and F; Python names are case-sensitive.

Open the explanation and practice

Select columns and keep a table

I can predict whether a column selection returns a Series or a DataFrame.

Show the tested example
df = students()
column = df["Score"]
table = df[["Score"]]
result = df[["Name", "Score"]]
print(type(column).__name__, column.shape)
print(type(table).__name__, table.shape)
print(result.head(3))

Watch for this: df["Name", "Score"] is not the same as df[["Name", "Score"]] for these ordinary columns.

Open the explanation and practice

Select rows by label or position

I can read selected records and one value using meaningful labels.

Show the tested example
df = students().set_index("Student_ID")
result = df.loc[["S001", "S003"], ["Name", "Score"]]
one_score = df.at["S003", "Score"]
print(result)
print("One score:", one_score)

Watch for this: set_index does not automatically check that IDs are unique; uniqueness needs its own check.

Open the explanation and practice

Filter with two conditions

I can translate both, either, and at least into correct filter logic.

Show the tested example
df = students()
mask = (df["Campus"] == "RAK") & (df["Score"] >= 80)
result = df.loc[mask, ["Student_ID", "Name", "Campus", "Score"]]
print(result)

Watch for this: Do not omit the parentheses around comparisons; operator precedence can change the expression.

Open the explanation and practice

Check membership or a range

I can express allowed categories, numerical ranges, and excluded categories.

Show the tested example
df = students()
in_program = df["Program"].isin(["AI"])
in_range = df["Score"].between(70, 90, inclusive="both")
result = df.loc[in_program & in_range, ["Name", "Program", "Score"]]
print(result)

Watch for this: Do not use Program == ["AI", "IT"] to test membership; use .isin(["AI", "IT"]).

Open the explanation and practice

Sort and choose a tie rule

I can make the order of tied records explicit.

Show the tested example
df = pd.DataFrame({
    "Name": ["Chloe", "Amira", "Bilal", "Dana"],
    "Score": [80, 80, 95, 80]
})
result = df.sort_values("Score", ascending=False, kind="stable")
print(result)

Watch for this: Alphabetical tie order must be requested explicitly; a stable sort preserves source order instead.

Open the explanation and practice

Update matching rows safely

I can modify selected values using one explicit loc assignment.

Show the tested example
df = students()
mask = df["Student_ID"] == "S004"
df.loc[mask, "Score"] = 67
result = df.loc[mask, ["Student_ID", "Name", "Score"]]
print(result)

Watch for this: Repeatedly running an incremental update on the same table would repeat the bonus; this cell starts with a fresh table each time.

Open the explanation and practice

Convert messy numbers

Audit conversion failures before deciding how to handle them.

Show the tested example
source = pd.Series(["88", "absent", None, "72"], dtype="string")
numbers = pd.to_numeric(source, errors="coerce")
invalid = source.notna() & numbers.isna()
result = pd.DataFrame({"Original": source, "Number": numbers, "Invalid_text": invalid})
print(result)

Watch for this: Do not silently turn every conversion failure into zero.

Open the explanation and practice

Understand missing, empty, and zero

Define what missing means in your dataset.

Show the tested example
values = pd.Series([0, None, "", pd.NA, np.nan], dtype="object")
result = values.isna()
print(result.tolist())
print("Missing count:", result.sum())

Watch for this: Use .isna(), not == np.nan, to detect missing values.

Open the explanation and practice

Find repeated IDs

Choose a key and a documented conflict rule.

Show the tested example
df = pd.DataFrame({"Student_ID": ["S001", "S001", "S002"], "Updated": ["2026-10-01", "2026-10-03", "2026-10-02"], "Score": [80, 88, 72]})
result = df.loc[df.duplicated(subset=["Student_ID"], keep=False)]
print(result)
print("Exact duplicates:", df.duplicated().sum())

Watch for this: In a transaction table, repeated student IDs may be expected; define what one row represents.

Open the explanation and practice

Clean whitespace and text

Standardize categories without destroying their meaning.

Show the tested example
df = raw_students()
df["Name"] = df["Name"].astype("string").str.strip()
result = df.loc[df["Student_ID"] == "S001", ["Student_ID", "Name"]]
print(result)

Watch for this: An unrecognized value passed through a dictionary map becomes missing; inspect unmatched labels.

Open the explanation and practice

Parse dates deliberately

Parse known date formats explicitly.

Show the tested example
source = pd.Series(["2026-09-01", "2026-09-03", "2026-02-30"], dtype="string")
result = pd.to_datetime(source, format="%Y-%m-%d", errors="coerce")
print(result)
print("Unparsed dates:", result.isna().sum())

Watch for this: Inspect parsing failures; errors="coerce" does not repair the source date.

Open the explanation and practice

Map labels from a dictionary

Check what a lookup leaves unmatched.

Show the tested 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)

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

Open the explanation and practice

Summarize each group

Summarize each group with readable labels and counts.

Show the tested example
df = students()
result = df.groupby("Campus", as_index=False).agg(Students=("Student_ID", "size"), Mean_score=("Score", "mean"))
print(result.round(2))

Watch for this: A mean without its supporting count can hide a very small group.

Open the explanation and practice

Return a group result to each row

Use transform when the result belongs beside the original records.

Show the tested example
df = students()
df["Program_mean"] = df.groupby("Program")["Score"].transform("mean")
df["Difference"] = df["Score"] - df["Program_mean"]
result = df[["Student_ID", "Program", "Score", "Program_mean", "Difference"]]
print(result.round(2))

Watch for this: A group-relative label and a fixed-threshold label answer different questions.

Open the explanation and practice

Audit a join

Use indicator to make unmatched records visible.

Show the tested example
audit = students().merge(assessment(), on="Student_ID", how="outer", indicator=True, validate="one_to_one")
result = audit.loc[audit["_merge"].ne("both"), ["Student_ID", "_merge"]]
print(result)
print("All merged rows:", len(audit))

Watch for this: Pandas may match missing keys to other missing keys; audit or reject null keys before important merges.

Open the explanation and practice

Prevent a join from multiplying rows

Check key uniqueness before trusting merged row counts.

Show the tested example
bad_lookup = pd.concat([departments(), departments().iloc[[0]]], ignore_index=True)
blocked = False
try:
    students().merge(bad_lookup, on="Program", validate="many_to_one")
except pd.errors.MergeError:
    blocked = True
    print("Blocked: Program is not unique in the lookup.")
result = students().merge(bad_lookup, on="Program")
print("Rows without validation:", len(result))

Watch for this: validate checks the relationship; it does not decide how to repair conflicting records.

Open the explanation and practice

Stack batches of rows

Use concat for batches with the same meaning and schema.

Show the tested example
df = students()
first = df.iloc[:6]
second = df.iloc[6:]
result = pd.concat([first, second], ignore_index=True)
print(result[["Student_ID", "Name"]])

Watch for this: Build a list of batches and concatenate once instead of repeatedly growing a DataFrame in a loop.

Open the explanation and practice

Change wide data to long

Long form keeps one measured value per row.

Show the tested example
wide = students().head(3)[["Student_ID", "Score", "Attendance"]]
result = wide.melt(id_vars="Student_ID", value_vars=["Score", "Attendance"], var_name="Measure", value_name="Value")
print(result)

Watch for this: A shared Value column may contain measures with different units; keep and use Measure.

Open the explanation and practice

Read and export CSV

Inspect imports and choose export columns deliberately.

Show the tested example
df = pd.read_csv(DATA_DIR / "students.csv", dtype={"Student_ID": "string"})
result = df.loc[df["Score"].ge(85), ["Student_ID", "Name", "Score"]]
result.to_csv(OUTPUT_DIR / "high_scores.csv", index=False)
print(result)
print("Saved outputs/high_scores.csv")

Watch for this: CSV does not preserve every pandas dtype. Check types and missing-value rules when reloading.

Open the explanation and practice

Select an Excel sheet

Excel I/O needs a workbook path and the correct worksheet.

Show the tested example
df = pd.read_excel(DATA_DIR / "students.xlsx", sheet_name="Students", engine="openpyxl")
result = df.groupby("Campus", as_index=False)["Score"].mean()
result.to_excel(OUTPUT_DIR / "campus_report.xlsx", sheet_name="Summary", index=False, engine="openpyxl")
print(result.round(2))

Watch for this: If openpyxl is missing, install the provided requirements before running this cell.

Open the explanation and practice

Query a SQLite table

Pandas can receive SQL results without loading every source row.

Show the tested example
with sqlite3.connect(":memory:") as connection:
    students().to_sql("students", connection, index=False, if_exists="replace")
    result = pd.read_sql_query(
        "SELECT Student_ID, Name, Score FROM students WHERE Score >= ? ORDER BY Score DESC",
        connection, params=(85,))
print(result)

Watch for this: Only use if_exists="replace" here on the temporary classroom table; on a real database it replaces the existing table.

Open the explanation and practice

Expose missing calendar dates

A gap in records is not automatically a zero.

Show the tested example
df = attendance()
df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d")
result = df.set_index("Date")["Visitors"].sort_index().asfreq("D")
print(result)

Watch for this: asfreq does not aggregate. Ensure the date index is unique before requesting a frequency.

Open the explanation and practice

Calculate a rolling average

Window size and missing-data rules both affect a moving average.

Show the tested example
df = attendance()
df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d")
daily = df.set_index("Date")["Visitors"].sort_index().asfreq("D")
result = pd.DataFrame({"Visitors": daily, "Mean_3_Days": daily.rolling(3, min_periods=3).mean()})
print(result.round(2))

Watch for this: Sort time data before rolling. Otherwise the window follows an unintended order.

Open the explanation and practice

Flatten nested JSON

Verify semantic equality after changing a data representation.

Show the tested example
path = DATA_DIR / "public" / "penguins.nested.json"
payload = json.loads(path.read_text(encoding="utf-8"))
result = pd.json_normalize(payload, sep=".")
print(result.head(3))
print("Columns:", result.columns.tolist())
print("Known body masses:", result["measurements.body_mass_g"].count())

Watch for this: The nested structure is a course-created representation, not a claim about an upstream research API.

Open the explanation and practice