最直接的布尔索引df[df['age'] > 18]易因NaN(返回False而非跳过)和字符串类型(导致报错或ASCII比较)出错;应先检查dtype、用pd.to_numeric强转,再用notna()显式处理空值,并严格使用&/|/~及括号组合多条件。
![pandas怎么按条件选行_df[df[\'age\']>18]布尔索引与多条件(&/|)](https://img.php.cn/upload/article/000/969/633/177354750231552.png)
用 df[df['age'] > 18] 选行,最直接但容易漏掉空值和类型问题
这个写法本身没错,但实际跑起来常发现结果比预期少——多半是 'age' 列里有 NaN,而布尔索引遇到 NaN 会直接返回 False,不是跳过,是“算作不满足”。比如 3 > 18 是 False,NaN > 18 也是 False,但你可能想保留这些空行或单独处理。
另外如果 'age' 是字符串类型(比如读 CSV 时没指定 dtype),'25' > 18 会报错或隐式转成 ASCII 比较,结果完全不可信。
- 先检查类型:
df['age'].dtype,不是int64或float64就得用pd.to_numeric(df['age'], errors='coerce')强转 - 想保留空值?改用
df[df['age'].fillna(-1) > 18]或更稳妥的df[df['age'].gt(18, fill_value=-1)] - 真要排除空值再筛选,明确写成
df[df['age'].notna() & df['age'] > 18](注意:这里必须用&,不能用and)
多条件组合必须用 &、|、~,别碰 and/or/not
写 df[df['age'] > 18 and df['city'] == 'Beijing'] 会直接报 ValueError: The truth value of a Series is ambiguous。因为 Pandas 的 Series 不支持 Python 原生逻辑运算符,它们不知道该对整个序列判真还是逐元素算。
&、|、~ 是逐元素位运算符,在 Pandas 里被重载为布尔索引的“且/或/非”,但要求每个条件必须用括号包住——否则运算优先级出错,比如 df['age'] > 18 & df['city'] == 'Beijing' 实际等价于 df['age'] > (18 & df['city']) == 'Beijing',肯定报错或结果错乱。
- 正确写法只有这一种:
df[(df['age'] > 18) & (df['city'] == 'Beijing')] - “或”用
|:(df['age'] - “非”用
~:~df['city'].isin(['Beijing', 'Shanghai']),不是not df[...] - 三个以上条件?老老实实每组都加括号,别省,省了就是 debug 半小时
query() 方法更适合复杂多条件,但要注意变量传入和性能边界
当条件变多、带变量、或者想写得像 SQL 一样可读,df.query("age > 18 and city in @cities") 比嵌套括号清爽太多。它自动处理引号、变量注入(用 @ 前缀)、甚至支持表达式如 age + years_exp > 30。
但它不是万能的:字符串列含空格或特殊字符(比如列名是 'user id')就得用反引号:df.query("`user id` > 100");而且底层用 numexpr 加速,对超大 DataFrame(千万行+)确实快,但小数据集反而略慢于普通布尔索引,因为有解析开销。
- 变量必须显式传入:
cities = ['Beijing', 'Shanghai']; df.query("city in @cities") - 列名含空格/符号:用反引号包裹,
`first name` == 'Alice' - 避免在
query()里调函数(如df.query("city.str.upper() == 'BEIJING'")),它不支持方法链,得先算好新列再查 - 调试时如果报
UndefinedVariableError,八成是变量没加@,或者作用域不对(比如在函数内没声明global)
用 loc 配合布尔条件,才能同时选行又选列,别只盯 []
df[df['age'] > 18] 只能返回整行,但实际中往往只要其中几列,比如只看 'name' 和 'city'。这时候硬套 df[df['age'] > 18][['name', 'city']] 看似可行,但属于链式索引(chained indexing),Pandas 会警告 SettingWithCopyWarning,后续赋值可能不生效——因为你拿到的可能是视图也可能是副本,行为不确定。
唯一稳的方式是用 .loc 一次性完成行列筛选:df.loc[df['age'] > 18, ['name', 'city']]。左边是行条件,右边是列名列表,清晰、安全、无警告。
- 要改值?也必须用
loc:df.loc[df['age'] > 18, 'status'] = 'adult' - 列名是单个字符串,右边可以直接写
'name';多个就写列表['name', 'city'] - 如果行条件结果为空,
loc返回空 DataFrame,不会报错,这点比直接切片友好 - 别用
iloc替代——它是按位置索引,和布尔条件无关,混用等于自找麻烦
NaN 悄悄吃掉数据、括号漏一个、或者 and 手滑打出来——这些点没 warning,但结果错得毫无征兆。









