Python Pandas:DataFrame多字符串映射仅单个生效的问题排查
问题原因分析
你遇到的核心问题是**fln()、lpg()、hwl()这三个函数会互相覆盖对方的修改结果**,而且完全没有做条件判断——不管当前行的序列号符合哪个规则,只要调用了这些函数,就会强制用对应位置的切片去执行映射,不匹配的行直接变成NaN,最后执行的那个函数会覆盖之前所有对New Meter Model和New Meter Manufacturer的修改。
举个直观的例子:
- 如果先调用
fln(),它会用序列号最后8位匹配规则,给符合条件的行赋值; - 接着调用
lpg(),它会用序列号最后2位匹配,这时候哪怕之前fln()已经赋值过的行,只要最后两位不是2061,就会被改成NaN; - 最后调用
hwl()的话,同理,只有最后两位是1914的行会被赋值,其他行(包括之前被fln或lpg处理过的)都会被覆盖成NaN。
这就是为什么你看到有时候只有某一个映射生效——本质上取决于你最后执行的是哪个函数,或者刚好哪部分数据匹配了最后那个函数的规则。
另外注意:原代码里lpg()和hwl()用str[-2:]去匹配"2061"和"1914"明显有问题,最后2位只能是两位字符,不可能匹配4位的字符串,这大概率是你写代码时的笔误,后面的修复方案会默认调整为合理的切片长度(比如取最后4位),你可以根据实际序列号规则再修改。
修复方案
你需要把这三个函数的逻辑合并,根据序列号的特征做条件判断,只对符合规则的行应用对应的映射,避免互相覆盖。这里提供两种可行的方式:
方式1:使用loc+掩码做精准赋值(推荐,效率更高)
这种方式适合规则明确的场景,处理大数据集时性能更好:
import numpy as np def map_meter_info(): # 处理FLN规则:序列号最后8位匹配指定值 fln_mask = gas_data["New Serial Number"].str[-8:].isin(["2000", "2100", "1800", "1900"]) gas_data.loc[fln_mask, "New Meter Model"] = gas_data.loc[fln_mask, "New Serial Number"].str[-8:].map({ "2000": "G4SZV-2", "2100": "G4SZV-2", "1800": "G4SZV-1", "1900": "G4SZV-1" }) gas_data.loc[fln_mask, "New Meter Manufacturer"] = "FLN" # 处理LPG规则:序列号最后4位为2061 lpg_mask = gas_data["New Serial Number"].str[-4:] == "2061" gas_data.loc[lpg_mask, "New Meter Model"] = "G470" gas_data.loc[lpg_mask, "New Meter Manufacturer"] = "LPG" # 处理HWL规则:序列号最后4位为1914 hwl_mask = gas_data["New Serial Number"].str[-4:] == "1914" gas_data.loc[hwl_mask, "New Meter Model"] = "BK-G4E" gas_data.loc[hwl_mask, "New Meter Manufacturer"] = "HWL" logging.info('Updated meter manufacturer codes and meter models based on serial number rules')
方式2:使用apply逐行判断(适合复杂规则)
如果你的序列号规则有更多特殊情况,逐行处理会更灵活:
def get_meter_details(row): serial = row["New Serial Number"] # 先检查FLN规则 last8 = serial[-8:] if len(serial) >= 8 else "" if last8 in ["2000", "2100", "1800", "1900"]: model_map = {"2000": "G4SZV-2", "2100": "G4SZV-2", "1800": "G4SZV-1", "1900": "G4SZV-1"} return pd.Series([model_map[last8], "FLN"]) # 再检查LPG规则 last4 = serial[-4:] if len(serial) >= 4 else "" if last4 == "2061": return pd.Series(["G470", "LPG"]) # 最后检查HWL规则 if last4 == "1914": return pd.Series(["BK-G4E", "HWL"]) # 都不匹配则保留原有值(或设置默认值) return pd.Series([row["New Meter Model"], row["New Meter Manufacturer"]]) def map_meter_info(): gas_data[["New Meter Model", "New Meter Manufacturer"]] = gas_data.apply(get_meter_details, axis=1) logging.info('Updated meter manufacturer codes and meter models based on serial number rules')
额外注意事项
- 主逻辑里只需要调用合并后的
map_meter_info()函数,不要再单独调用原来的fln()、lpg()、hwl()了; - 原代码中
add_location()已经对New Meter Model做过一次映射,记得确认执行顺序:如果add_location()的映射是优先级更低的默认值,应该先执行add_location(),再执行map_meter_info(),这样序列号匹配的行会覆盖默认值; - 处理前可以先检查
New Serial Number列的格式,比如有没有空值、长度不足的情况,避免切片报错。
内容的提问来源于stack exchange,提问作者Mizanur Choudhury
相关产品推荐
相关产品推荐

