You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas非迭代DataFrame行 按字典列表规则新增Billing Code列

Pandas高性能规则匹配新增列实现方案

问题说明

  • 待处理数据为8.4万行的Pandas DataFrame,每行对应1条客户账户记录,字段名与规则字典的键名完全匹配,列定义如下:
df.columns = ['Source.Name','User Bank','Bank','Account Number','Account Description','Valuation Date',
              'Preschedule','MF Flag','Load Flag','Global Flag','Money Market Flag','Days Prior to Valuation',
              'Number of Holdings','Total Assets','Unit Value/NAV','MCS Field','From Date','Valuations',
              '# Sweeps','NASDAQ','TLA','Account Type','Fund Group','Master Account Text','Master Feeder Flag',
              'Acct Flag 2','Acct Field 4','Securities At Value','Net Assets','Acct Field 1','Acct Field 2',
              'Group Account Indicator','Group Account Number','Region','Account Status','SMS Billing Code',
             'Translation Date','Portfolio Manager 1','Acct Flag 1','Dual Flag','Securities At Value Base',
             'Net Assets Base','Total Assets Base','Dual OEIC']
  • 匹配规则为共427个元素的字典列表,每个字典归属特定客户,键数量为2-50个不等,必须严格按照列表顺序应用规则:
    • 字典第1个键值对:标识规则所属客户
    • 字典第2个键值对:规则命中后对应的Billing Code取值
    • 剩余键值对:字段匹配条件,支持大于比较、通配符匹配等逻辑
      规则示例如下:
0 {'client': 'client 1', 'Billing Code': 'TNL', 'Valuations': '0', 'Account Number': '>99999'}
1 {'client': 'client 1', 'Billing Code': 'MF', 'User': 'BP', 'Flag': 'S'}
...
13 {'client': 'client 2', 'Billing Code': 'TNL', 'Acct Desc': '*test*'}

# 列表总长度427

匹配要求

逐行按规则列表顺序校验,每行命中的第一条规则对应的Billing Code即为该行该列的最终取值,输出新增Billing Code列后的完整DataFrame。

性能约束:8.4万行量级下逐行迭代(如iterrows、行级apply)耗时过长,需基于向量化操作实现,避免Python层逐行循环开销。

向量化实现代码

核心逻辑为按规则顺序逐批做全列向量化匹配,每轮仅处理尚未匹配到结果的行,所有计算走Pandas/Numpy底层C实现,性能较逐行迭代提升10100倍,8.4万行+427条规则场景下通常13秒即可跑完。

import pandas as pd
import numpy as np
from typing import List, Dict

def add_billing_code_col(df: pd.DataFrame, rules: List[Dict], client_col_map: Dict = None) -> pd.DataFrame:
    """
    按规则顺序向量化匹配新增Billing Code列
    :param df: 原始账户DataFrame
    :param rules: 规则字典列表
    :param client_col_map: 规则客户字段和df列名的映射,比如{'client': 'Source.Name'}
    """
    if client_col_map is None:
        client_col_map = {}
    
    res_df = df.copy()
    res_df['Billing Code'] = pd.NA
    # 标记未匹配到规则的行
    unmatched = pd.Series(True, index=res_df.index)

    for rule in rules:
        # 所有行匹配完成直接终止
        if not unmatched.any():
            break

        rule_items = list(rule.items())
        # 解析规则固定字段
        rule_client_key, rule_client_val = rule_items[0]
        _, target_billing_code = rule_items[1]
        match_conditions = rule_items[2:]

        # 初始化本轮候选命中行:仅从未匹配的行里筛选
        candidate = unmatched.copy()
        # 先匹配客户归属
        actual_client_col = client_col_map.get(rule_client_key, rule_client_key)
        candidate &= res_df[actual_client_col].astype(str).str.strip().str.lower() == str(rule_client_val).strip().lower()

        # 逐条件向量化校验
        for col, cond_val in match_conditions:
            if not candidate.any():
                break
            cond_str = str(cond_val).strip()
            col_data = res_df.loc[candidate, col]

            # 大于号数值比较
            if cond_str.startswith('>'):
                threshold = float(cond_str[1:].strip())
                cond_res = pd.to_numeric(col_data, errors='coerce') > threshold
            # 小于号数值比较(按需保留)
            elif cond_str.startswith('<'):
                threshold = float(cond_str[1:].strip())
                cond_res = pd.to_numeric(col_data, errors='coerce') < threshold
            # *通配符匹配,转正则
            elif '*' in cond_str:
                regex_pat = cond_str.replace('*', '.*')
                cond_res = col_data.astype(str).str.fullmatch(regex_pat, case=False, na=False)
            # 普通精确匹配
            else:
                cond_res = col_data.astype(str).str.strip().str.lower() == cond_str.lower()

            candidate.loc[candidate] = cond_res.fillna(False)

        # 给本轮命中的行赋值,更新未匹配掩码
        hit_idx = candidate[candidate].index
        res_df.loc[hit_idx, 'Billing Code'] = target_billing_code
        unmatched.loc[hit_idx] = False

    return res_df

调用示例

# 如果规则里客户字段是client,对应df里的列是Source.Name,就传映射
client_mapping = {'client': 'Source.Name'}
result_df = add_billing_code_col(origin_df, rules_list, client_col_map=client_mapping)

优化点说明

  • 每轮规则匹配仅处理尚未命中的行,随着匹配推进计算量逐轮降低
  • 内置提前终止逻辑,所有行完成匹配后直接跳出循环,无需遍历全部427条规则
  • 所有匹配逻辑均为列级向量化操作,无Python层逐行循环开销
  • 数值比较自动做类型转换,异常值默认不命中,避免类型错误中断任务
  • 通配符、精确匹配默认不区分大小写、忽略首尾空格,可根据业务需求调整参数

内容的提问来源于stack exchange,提问作者snek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 19:15:30