loc筛选必须同时指定行条件和列参数,如df.loc[条件, 列列表];链式调用易致警告或错误,布尔运算须用&、|、~并加括号,修改数据应优先用loc原地赋值。

用 loc 筛选行并选特定列,必须写成 df.loc[条件, 列列表]
很多人写成 df.loc[条件][列列表] 或 df[条件].loc[:, 列列表],结果要么报错,要么返回意外的 Series 或视图问题。根本原因是 loc 是二维索引器,行和列必须**同时指定**在同一个 loc 调用里,否则链式操作会触发 SettingWithCopyWarning 或隐式拷贝。
常见错误现象:KeyError: "['col_a' 'col_b'] not in index"(误把列名当行标签传给第一个参数),或返回 Series(只传了行条件,没传列参数)。
-
df.loc[df['age'] > 25, ['name', 'city']]✅ 正确:布尔数组 + 列名列表 -
df.loc[df['age'] > 25, 'name']✅ 返回Series,不是DataFrame;要保持 DataFrame 用['name'] -
df[df['age'] > 25].loc[:, ['name', 'city']]⚠️ 不推荐:先切片再loc,可能触发副本警告,且性能略差
loc 中列参数传字符串、列表、切片的区别
列部分不只支持 ['a', 'b'],但不同写法语义和返回类型不同,直接影响后续操作。
- 传单个字符串如
'name'→ 返回Series;即使原DataFrame只有一列,也**不是单列DataFrame** - 传列表如
['name']或['name', 'age']→ 返回DataFrame,列顺序按列表顺序,允许重复或乱序 - 传切片如
'name':'salary'→ 按列位置(非标签名)切片,要求列名在DataFrame.columns中连续且有序;'salary':'name'会返回空 - 传
:→ 选所有列,等价于省略列参数,但显式写出更清晰,尤其在复杂条件中
布尔条件里混用 & 和 and 会直接报错
这是 Pandas 新手最常卡住的地方:and、or、not 不能用于 Series 布尔运算,Python 会尝试对整个 Series 做真值判断,立刻抛出 ValueError: The truth value of a Series is ambiguous。
- ✅ 正确:用
&(不是&&)、|(不是||)、~(不是!),且每个条件必须括号包裹:df.loc[(df['age'] > 25) & (df['city'] == 'Beijing'), ['name']] - ❌ 错误:
df.loc[df['age'] > 25 and df['city'] == 'Beijing', ...]→ 报错 - ⚠️ 注意:
in也不能直接用在Series上,要用isin():df.loc[df['city'].isin(['Beijing', 'Shanghai']), ...]
筛选后修改数据,别忘了检查是否是视图还是副本
用 loc 筛选出来的结果,可能是原 DataFrame 的视图(view)或副本(copy),这取决于底层内存布局和 Pandas 版本。直接赋值修改时,有时改不动,有时改了但原 DataFrame 不变,有时又触发 SettingWithCopyWarning。
- 安全写法:明确用
.copy()再改,或用.loc**原地赋值**:df.loc[df['age'] > 30, 'status'] = 'senior' - 危险写法:
subset = df.loc[df['age'] > 30, ['name']]→ 然后subset['name'] = 'xxx',可能静默失败或报警 - 验证方式:打印
subset._is_view(非公开属性,仅作调试参考),或直接改后看原df是否变化
真正麻烦的不是语法,是当你在函数里传入一个筛选后的子集,又想就地更新它——这时候得提前约定输入是否可变,或者统一加 .copy() 避坑。










