The single biggest source of confusion in Pandas is how to pick the rows and columns you want. The library exposes three different indexing styles, and beginners blend them in ways that produce wrong answers without errors. Here's the working framework.
The three indexers
df['col']— column access by label. Returns a Series.df.loc[row_label, col_label]— label-based selection. Both row and column are addressed by their labels (or boolean masks).df.iloc[row_pos, col_pos]— position-based selection. Both row and column are addressed by integer position, like a NumPy array.
Use .loc for label-based work, .iloc for position-based work, and never mix them in your head. The legacy df.ix attribute is gone. The chained-indexing style df[df.col > 0]['other'] = ... is a classic source of SettingWithCopyWarning — which Pandas 3.0 has tightened the screws on under Copy-on-Write semantics.
The Copy-on-Write era
Pandas 3.0 made Copy-on-Write (CoW) the default. Practical effect: chained assignments that used to silently mutate the parent DataFrame now either mutate a copy (so your change disappears) or raise a clear error. The fix is the same fix it's always been — use a single .loc with both row and column on the left side of the assignment.