在Pandas中实现字符串部分匹配并提取匹配值至新列的技术问询
解决DataFrame中按行匹配字符生成新列的问题
首先看你的需求:要把每行letter列的字符,和同一行其他列(info1-info4)的字符串开头匹配,把匹配到的内容放到new列,没匹配就填NaN。你的初始尝试用了isin和any,但这个方法是检查整个单元格的值是否在letter列的全局集合里,而我们需要的是单元格字符串的前缀是否等于当前行的letter值,所以这个思路不对。
下面给你两种可行的解决方案:
方法一:逐行遍历匹配(直观易懂)
我们可以用apply函数逐行处理,对每行来说,取出letter的值,然后遍历其他列的元素,找到以该letter开头的那个元素,找不到就返回NaN:
import pandas as pd d = {'letter':['R','V','Q','M','F','K'], 'info1':['K:2.3','T:1.3','L:2.4','B:7.4','S:2.3','K:4.4'], 'info2':['R:3.2','N:2.1','B:0.3','T:0.9','J:0.003','S:1.223'], 'info3':['X:45','V:32.4','H:0.04','M:3.34','T:2.2','T:3.456'], 'info4':['A:1.7','Z:1.2345','T:9.5','O:3,2','J:3.334','G:345']} df1 = pd.DataFrame(d) def find_match(row): target = row['letter'] # 遍历除了letter之外的列 for col in ['info1', 'info2', 'info3', 'info4']: val = row[col] if val.startswith(target): return val return pd.NA # 也可以用np.nan df1['new'] = df1.apply(find_match, axis=1)
运行后就能得到你想要的结果:
letter info1 info2 info3 info4 new 0 R K:2.3 R:3.2 X:45 A:1.7 R:3.2 1 V T:1.3 N:2.1 V:32.4 Z:1.2345 V:32.4 2 Q L:2.4 B:0.3 H:0.04 T:9.5 NaN 3 M B:7.4 T:0.9 M:3.34 O:3,2 M:3.34 4 F S:2.3 J:0.003 T:2.2 J:3.334 NaN 5 K K:4.4 S:1.223 T:3.456 G:345 K:4.4
方法二:向量化操作(更高效,适合大数据)
如果你的DataFrame行数很多,apply的效率可能不够,我们可以用向量化的方法,先对每个info列生成匹配掩码,然后提取对应的值:
# 提取所有info列 info_cols = [col for col in df1.columns if col.startswith('info')] # 对每个info列,生成是否以当前行letter开头的布尔矩阵 matches = df1[info_cols].apply(lambda x: x.str.startswith(df1['letter'], na=False)) # 找到每行第一个匹配的列,提取对应的值 df1['new'] = df1[info_cols].where(matches).bfill(axis=1).iloc[:, 0]
这个方法的原理是:
- 先创建一个布尔矩阵,每个元素表示对应info列的单元格是否以该行的letter开头
- 用
where把不匹配的位置变成NaN,然后用bfill(或ffill)把每行第一个匹配的值填充到第一列 - 最后提取第一列就是我们要的结果
为什么你的初始代码不行?
你原来的代码df1['new'] = df1.drop("letter", 1).isin(df1["letter"]).any(1)存在两个问题:
- 它是检查每个单元格的值是否完全等于整个
letter列的任意值,不是当前行的letter值 - 最终返回的是每行是否有匹配的布尔值(True/False),而不是你需要的匹配字符串
这就是为什么这个方法达不到你想要的效果啦。
内容的提问来源于stack exchange,提问作者Chip
相关产品推荐
相关产品推荐

