
本文详解如何基于两列字符串的包含关系(如 part_name 是否为 full_name 的子串)筛选或删除 DataFrame 行,解决 str.contains() 不支持 Series 作为 pattern 的常见报错。
本文详解如何基于两列字符串的包含关系(如 `part_name` 是否为 `full_name` 的子串)筛选或删除 dataframe 行,解决 `str.contains()` 不支持 series 作为 pattern 的常见报错。
在 Pandas 中,当需要根据「某列字符串是否包含另一列字符串」进行行过滤时(例如:保留 part_name 不出现于 full_name 中的行),直接使用 df['full_name'].str.contains(df['part_name']) 会触发 TypeError: unhashable type: 'Series' —— 因为 .str.contains() 的 pat 参数仅接受标量(如字符串、正则模式),不支持传入整个 Series。
根本原因在于:该方法设计为对单个 pattern 进行向量化匹配,而非逐行执行“左列是否包含右列”的动态判断。此时需改用逐行逻辑(row-wise evaluation),推荐使用 DataFrame.apply() 配合 axis=1 实现。
以下为完整可运行示例:
import pandas as pd
# 构建示例数据
df = pd.DataFrame({
"city_code": ["34", "36", "89", "34"],
"full_name": ["WXYZ(24)", "ZYXW", "YZWX", "WXYZ(24)"],
"part_name": ["WXYZ", "ABCD", "YZWX", "ABCD"]
})
# ✅ 正确做法:逐行判断 part_name 是否 NOT in full_name
mask = df.apply(lambda row: row["part_name"] not in row["full_name"], axis=1)
result = df[mask].reset_index(drop=True)
print(result)输出:
city_code full_name part_name 0 36 ZYXW ABCD 1 34 WXYZ(24) ABCD
? 注意:我们构造的是 保留条件(not in),因此最终结果即为题目所求的“part_name 不被 full_name 包含”的行。
⚠️ 关键注意事项
- 性能权衡:apply(..., axis=1) 属于 Python 级循环,在大数据集(>10⁵ 行)上可能较慢。若追求极致性能,可考虑 numba 加速或预编译正则(但本场景中子串匹配逻辑简单,通常无需过度优化)。
-
空值处理:若 full_name 或 part_name 可能含 NaN,x not in y 在遇到 NaN 时会返回 True(因 NaN == NaN 为 False,且 NaN in [...] 恒为 False)。如需严谨处理缺失值,建议先填充或显式过滤:
mask = df.notna().all(axis=1) & df.apply( lambda r: r["part_name"] not in r["full_name"], axis=1 ) -
大小写敏感性:上述逻辑默认区分大小写。如需忽略大小写,可统一转小写后比较:
lambda r: r["part_name"].lower() not in r["full_name"].lower()
✅ 总结
| 方法 | 是否推荐 | 说明 |
|---|---|---|
| str.contains(df['part_name']) | ❌ | 直接报错,语法非法 |
| apply(..., axis=1) + in/not in | ✅ | 清晰、可靠、语义直观,适合大多数场景 |
| 向量化字符串操作(如 np.char.find) | △ | 需手动对齐索引、处理 dtype,复杂度高,不推荐初学者使用 |
掌握这种基于双列动态字符串关系的过滤逻辑,是 Pandas 数据清洗中处理命名规范、代码映射、标签校验等任务的关键能力。务必优先保证逻辑正确性与可读性,再视数据规模决定是否引入高级优化手段。










