应使用 ~df['col'].str.contains(..., na=False) 删除含特定词的行,关键要设 na=False 避免 NaN 导致 TypeError,且 query() 不支持 contains,isin() 仅精确匹配而非子串搜索。

用 str.contains() 配合 ~ 删除含特定词的行
直接删:先筛出含关键词的行,再用取反逻辑过滤掉。核心是 str.contains() 返回布尔 Series,~ 对它取反即可得到“不包含”的掩码。
常见错误是忘了加 na=False ——如果列里有 NaN,str.contains() 默认返回 NaN,而 ~NaN 会报错 TypeError: bad operand type for unary ~: 'float'。
- 必须显式传
na=False,让缺失值统一视为False,取反后为True(即保留 NaN 行)或按需处理 - 正则特殊字符(如
.、*、?)默认被当作正则,想匹配字面量要加regex=False - 大小写敏感默认开启,不区分大小写加
case=False
示例:df = df[~df['text'].str.contains('error', na=False, case=False)]
为什么不用 query()?它不支持原地字符串模糊匹配
query() 写起来简洁,但它的字符串方法只支持 .str.startswith()、.str.endswith() 和 .str.fullmatch() 这类确定性匹配,没有 .str.contains() 的等价写法。硬写 query("text.str.contains('x')") 会报 UndefinedVariableError ——query 不解析嵌套属性调用。
所以别绕弯子,该用布尔索引就用布尔索引。
isin() 和 contains() 别混用:一个是精确匹配,一个是子串搜索
有人看到“删含某几个词的行”,第一反应是 isin(),但它只判断整单元格是否等于某个值,不是找子串。比如 df[~df['text'].isin(['error', 'fail'])] 只删掉整个值刚好是 'error' 或 'fail' 的行,不会删 'system error occurred' 这种。
- 要删“包含任意一个关键词”的行,用
|拼接多个contains(),或用正则pattern = 'error|fail' - 要删“同时包含多个词”的行,得链式调用或用
apply()+all(),但性能差,慎用
性能注意:大表上避免重复计算,必要时缓存布尔掩码
~df['col'].str.contains(...) 每次都重新算一遍。如果后续还要基于同样条件做其他操作(比如统计剩余行数、再按另一列分组),建议先把掩码存成变量:
mask = ~df['text'].str.contains('temp', na=False)df_clean = df[mask].copy()print(f"删了 {len(df) - mask.sum()} 行")
尤其当 contains() 带复杂正则或 case=False 时,开销明显。另外,copy() 能避免 SettingWithCopyWarning,虽然不是本问题重点,但顺手加上更稳妥。
真正容易被忽略的是 na 参数的默认行为——很多人调完发现删少了或报错了才回头查文档,其实它才是最常踩的坑。

















