Get exactly the rows and columns you want. Master .loc and .iloc, boolean filtering, and the difference between label-based and position-based access.
Why: pandas has two selectors — .loc uses row/column LABELS, .iloc uses integer POSITIONS — and mixing them up is a classic bug. When: use .loc when you know the label or condition, .iloc when you want "the first 5 rows." Where: .loc is inclusive of its endpoint; .iloc is exclusive, like normal Python slicing.
import pandas as pd
df = pd.DataFrame(
{"age": [36, 29, 45, 41], "role": ["eng", "eng", "sci", "sci"]},
index=["ada", "linus", "grace", "alan"],
)
print(df.loc["grace"]) # row by label
print(df.loc["ada", "age"]) # 36 — label row + column
print(df.iloc[0]) # first row by position
print(df.iloc[0:2, 0]) # first 2 rows, first columnWhy: most selection is conditional — "rows where age > 40 and role is sci" — done with boolean masks, the pandas equivalent of a SQL WHERE. When: build a mask, then pass it to the DataFrame. Where: combine conditions with & and | (not and/or), and wrap each in parentheses.
# One condition:
print(df[df["age"] > 40])
# Multiple conditions — & and |, each wrapped in ():
mask = (df["age"] > 40) & (df["role"] == "sci")
print(df[mask])
# isin for a set of values:
print(df[df["role"].isin(["eng"])])Why: assigning to a slice of a slice sometimes edits a copy and silently does nothing — pandas warns you, and the fix is to select and assign in one .loc call. When: any time you filter then assign, use .loc[mask, col] = value. Where: this is the most common pandas gotcha; the single-step .loc form always works.
# WRONG — may edit a copy and warn "SettingWithCopyWarning":
# df[df["age"] > 40]["role"] = "senior"
# RIGHT — select rows and column in one .loc, then assign:
df.loc[df["age"] > 40, "role"] = "senior"
print(df)