如何在Pandas中通过函数或变量复用过滤器以消除代码重复、提升可维护性?
如何用函数复用Pandas中的过滤逻辑以优化重复代码?
我是Pandas新手,正尝试优化Python(基于Pandas)代码中的重复行。我希望将过滤/切片后的数据存入变量直接复用,理想情况下可以通过传入过滤器的函数来实现这一需求。
目前我的代码可读性与可维护性极差,但遵循固定模式:对DataFrame进行切片、为切片后的字段赋值、将结果导出至CSV。当前冗余代码如下:
df.loc[(df[field1].str.contains('Galvanized|Lead',flags=re.IGNORECASE) == True) & ((df[field2] == 'TP-returnForFull') & (df[field3] == 'NO')), reportErrorCodeField] = errCode df.loc[(df[field1].str.contains('Galvanized|Lead',flags=re.IGNORECASE) == True) & ((df[field2] == 'TP-returnForFull') & (df[field3] == 'NO')), reportErrorCodeDescField] = errDesc if len(df[(df[field1].str.contains('Galvanized|Lead',flags=re.IGNORECASE) == True) & ((df[field2] == 'TP-returnForFull') & (df[field3] == 'NO'))]) == 0: writeEmptyCSVrecordMessage(csvPath + '\\' + csvFileName, "ALL", errCode, "No records") else: df[reportFlds][(df[field1].str.contains('Galvanized|Lead',flags=re.IGNORECASE) == True) & ((df[field2] == 'TP-returnForFull') & (df[field3] == 'NO'))].to_csv(csvPath + '\\' + csvFileName, mode='a', header=False)
我已经尝试将重复的过滤条件存入变量,避免反复编写相同代码:
dataFilter = (df[field1].str.contains('Galvanized|Lead',flags=re.IGNORECASE) == True) & ((df[field2] == 'TP-returnForFull') & (df[field3] == 'NO')) df.loc[dataFilter, reportErrorCodeField] = errCode df.loc[dataFilter, reportErrorCodeDescField] = errDesc if len(df[dataFilter]) == 0: writeEmptyCSVrecordMessage(csvPath + '\\' + csvFileName, "ALL", errCode, "No records") else: df[reportFlds][dataFilter].to_csv(csvPath + '\\' + csvFileName, mode='a', header=False)
请问是否可以通过函数实现此类过滤器的复用,以进一步优化代码?
当然可以!把过滤逻辑封装成函数是提升代码可维护性和复用性的绝佳方式,甚至能让你的逻辑更灵活适配不同场景。
第一步:封装专属过滤函数
先针对你当前的需求写一个专用函数,把重复的判断逻辑打包进去,还能顺便简化一些冗余写法(比如str.contains本身就返回布尔值,不需要额外加== True):
import re def get_galvanized_lead_filter(df, field1, field2, field3): # 拆分条件让逻辑更清晰 has_target_keywords = df[field1].str.contains('Galvanized|Lead', flags=re.IGNORECASE) meets_return_conditions = (df[field2] == 'TP-returnForFull') & (df[field3] == 'NO') # 合并条件返回布尔索引 return has_target_keywords & meets_return_conditions
然后在主代码里调用这个函数就行,和你用变量的方式几乎一样,但逻辑被封装起来了,后续要修改过滤规则只需要改函数内部:
# 获取过滤条件 dataFilter = get_galvanized_lead_filter(df, field1, field2, field3) # 后续业务逻辑保持不变 df.loc[dataFilter, reportErrorCodeField] = errCode df.loc[dataFilter, reportErrorCodeDescField] = errDesc if df[dataFilter].empty: # 用empty属性替代len判断,可读性更好 writeEmptyCSVrecordMessage(f"{csvPath}\\{csvFileName}", "ALL", errCode, "No records") else: df[reportFlds][dataFilter].to_csv(f"{csvPath}\\{csvFileName}", mode='a', header=False)
第二步:升级为通用过滤函数
如果之后你还有类似的过滤需求(比如不同的关键词、不同的field2/field3值),可以把函数改得更通用,让它能适配多种场景:
def get_custom_filter(df, target_field, keyword_pattern, field2_val, field3_val): has_keywords = df[target_field].str.contains(keyword_pattern, flags=re.IGNORECASE) meets_field_conditions = (df['field2'] == field2_val) & (df['field3'] == field3_val) return has_keywords & meets_field_conditions
调用时只需要传入不同的参数即可:
# 原来的过滤逻辑 dataFilter = get_custom_filter(df, field1, 'Galvanized|Lead', 'TP-returnForFull', 'NO') # 其他类似的过滤需求(示例) anotherFilter = get_custom_filter(df, field1, 'Steel|Iron', 'TP-returnForPartial', 'YES')
额外小优化
- 用f-string替代字符串拼接,比如
f"{csvPath}\\{csvFileName}"比csvPath + '\\' + csvFileName更简洁易读; - 用
df[dataFilter].empty判断是否有匹配行,比len(df[dataFilter]) == 0更符合Pandas的惯用写法。
这样改造后,你的代码不仅更整洁,后续维护规则或者新增类似逻辑时也会轻松很多!
内容的提问来源于stack exchange,提问作者Michael Rut
相关产品推荐
相关产品推荐

