如何根据列表前缀匹配为DataFrame新增BAN列与FLAG列
问题描述
现有如下结构的DataFrame(含数万行数据):
CALL_START IMSI 0 24.07.2025 12:00:51 123456888888888 1 24.07.2025 17:58:57 123456999999999 2 24.07.2025 17:05:47 543214511111111 3 24.07.2025 17:02:30 543214522222222 4 24.07.2025 16:07:44 987654444444444 5 24.07.2025 16:07:49 777777444444444
用于匹配的列表较短(约100个元素,长度为5或6位),示例:
['123456', '54321', '98765']
需求:
检查IMSI列(字符串类型)是否以列表中的元素开头,满足以下任一方案即可:
- 新增两列:
BAN列存储匹配的列表元素,FLAG列设为True;未匹配则BAN设为None(或空字符串),FLAG设为False。 - 仅保留匹配结果(不含
FLAG列),示例输出:
CALL_START IMSI BAN 0 24.07.2025 12:00:51 123456888888888 123456 1 24.07.2025 17:58:57 123456999999999 123456 2 24.07.2025 17:05:47 543214511111111 54321 3 24.07.2025 17:02:30 543214522222222 54321 4 24.07.2025 16:07:44 987654444444444 98765
已知pd.Series.str.startswith()仅返回布尔值Series,无法满足需求,寻求可行方案。
解决方案
方法1:正则表达式提取(高效)
利用str.extract结合正则匹配前缀,适合处理大规模数据:
import pandas as pd # 初始化示例数据 df = pd.DataFrame({ 'CALL_START': ['24.07.2025 12:00:51', '24.07.2025 17:58:57', '24.07.2025 17:05:47', '24.07.2025 17:02:30', '24.07.2025 16:07:44', '24.07.2025 16:07:49'], 'IMSI': ['123456888888888', '123456999999999', '543214511111111', '543214522222222', '987654444444444', '777777444444444'] }) ban_list = ['123456', '54321', '98765'] # 生成正则匹配模式:匹配以任意ban元素开头的前缀 pattern = fr'^({"|".join(ban_list)})' df['BAN'] = df['IMSI'].str.extract(pattern) # 生成FLAG列 df['FLAG'] = df['BAN'].notna() # 仅保留匹配结果(不含FLAG列) matched_df = df.dropna(subset=['BAN']).drop(columns=['FLAG'])
- 正则
^表示字符串开头,|分隔所有备选前缀,str.extract会提取匹配到的前缀,未匹配则返回NaN(等价于None)。 FLAG列直接通过BAN列是否非空生成,逻辑简洁高效。
方法2:apply遍历匹配(直观)
因为匹配列表仅约100个元素,对万级数据来说性能完全够用,逻辑更易理解:
def find_matched_ban(imsi): for ban in ban_list: if imsi.startswith(ban): return ban return None df['BAN'] = df['IMSI'].apply(find_matched_ban) df['FLAG'] = df['BAN'].notna() # 仅保留匹配结果 matched_df = df[df['FLAG']].drop(columns=['FLAG'])
- 自定义函数遍历ban列表,返回第一个匹配的前缀,未匹配则返回
None。 - 后续生成
FLAG和过滤结果的逻辑与方法1一致。
方法3:利用str.startswith广播特性(进阶)
结合np.select将布尔匹配结果映射为对应前缀:
import numpy as np # 生成每个ban对应的布尔匹配Series conditions = [df['IMSI'].str.startswith(ban) for ban in ban_list] # 按顺序匹配,返回第一个满足条件的ban值,未匹配返回None df['BAN'] = np.select(conditions, ban_list, default=None) df['FLAG'] = df['BAN'].notna() # 仅保留匹配结果 matched_df = df[df['FLAG']].drop(columns=['FLAG'])
conditions是布尔Series列表,每个元素对应一个ban的匹配结果。np.select按顺序检查条件,返回第一个满足条件的ban值,无匹配则返回默认值。
内容的提问来源于stack exchange,提问作者urosdigital
相关产品推荐
相关产品推荐

