如何用Pandas将交易腿映射为结构化组合并处理重复与残差数据
问题:将交易腿归为结构化组合
我正在处理一个DataFrame,每行代表一条交易腿,需要把这些交易腿归类为结构化组合(structures)(即固定频次/组合的交易腿集合)。
现有数据统计
通过以下代码统计每个MainID下各GenStructure的频次:
slice_b.groupby("MainID")["GenStructure"].value_counts()
得到结果:
MainID GenStructure 2 Call 1 Put 1 Straddle 1 4 Put 1 Straddle 1 6 Call 1 Put 1 Straddle 1 10 Straddle 2 Call 1 Put 1 20 Call 1 Put 1 Straddle 1 21 Put 2 Call 1 23 Put 4 Call 2 24 Put 2 Call 1 86 Call 2 Put 2 87 Call 2 Put 2 97 Call 2 Put 2 98 Put 4 Call 3 99 Put 4 Call 2 100 Call 2 101 Call 4 Put 4 102 Put 4 Call 2 103 Call 2 108 Put 4 Call 2 111 Call 2 Put 2 114 Call 4 Put 4 145 Call 2 Put 2
初始映射方案
原本计划用以下字典做组合映射:
structure_fx_map = { ("Call", "Call", "Put", "Put"): "RR", ("Call", "Put", "Straddle"): "Strg", ("Call", "Call", "Put"): "CallStruct", ("Put", "Put", "Call"): "PutStruct", }
遇到的问题
但从统计结果看,每个MainID存在两种情况:
- 同一结构化组合出现多次
- 存在数据质量问题导致的1-2条剩余交易腿
期望结果
需要得到如下格式的输出:
| MainID | Struct | Count |
|---|---|---|
| 2 | 'Strg' | 1 |
| 4 | 'Put' | 1 |
| 4 | 'Straddle' | 1 |
| 6 | 'Strg' | 1 |
| 10 | 'Strg' | 1 |
| 10 | 'Straddle' | 1 |
解决方案
思路解析
核心是先对每个MainID的交易腿进行频次拆解:优先匹配完整的结构化组合,再处理剩余的零散交易腿。具体步骤:
- 将groupby统计结果转换为DataFrame,便于按
MainID分组处理 - 把原有的元组映射替换为频次模板(用
{交易类型: 所需数量}的形式),适配多频次场景 - 对每个
MainID,循环匹配结构化组合,计算可匹配次数并扣除对应频次,最后处理剩余交易腿
代码实现
import pandas as pd # 1. 将groupby统计结果转换为结构化DataFrame count_df = slice_b.groupby("MainID")["GenStructure"].value_counts().reset_index(name="Count") # 2. 定义结构化组合的频次模板:键是{交易类型: 所需数量}的不可变集合,值是组合名称 structure_template = { frozenset({"Call": 1, "Put": 1, "Straddle": 1}): "Strg", frozenset({"Call": 2, "Put": 2}): "RR", frozenset({"Call": 2, "Put": 1}): "CallStruct", frozenset({"Put": 2, "Call": 1}): "PutStruct", } # 3. 定义单个MainID的处理函数 def process_mainid(group): # 把当前MainID的交易腿转为{类型: 总数量}的字典 leg_counts = group.set_index("GenStructure")["Count"].to_dict() result_rows = [] # 优先匹配结构化组合 for template, struct_name in structure_template.items(): template_dict = dict(template) # 计算当前模板最多能匹配的次数 match_times = min(leg_counts.get(k, 0) // v for k, v in template_dict.items()) if match_times > 0: result_rows.append({ "MainID": group["MainID"].iloc[0], "Struct": f"'{struct_name}'", "Count": match_times }) # 扣除已匹配的交易腿数量 for k, v in template_dict.items(): leg_counts[k] -= match_times * v # 移除数量为0的交易腿 leg_counts = {k: v for k, v in leg_counts.items() if v > 0} # 处理剩余的零散交易腿 for leg_type, cnt in leg_counts.items(): result_rows.append({ "MainID": group["MainID"].iloc[0], "Struct": f"'{leg_type}'", "Count": cnt }) return pd.DataFrame(result_rows) # 4. 按MainID分组处理,合并最终结果 final_result = count_df.groupby("MainID").apply(process_mainid).reset_index(drop=True) # 查看结果 print(final_result)
代码说明
- 使用
frozenset作为模板键,避免交易腿顺序影响匹配结果 - 先匹配完整组合再处理剩余腿,保证结构化组合优先被识别
- 零散交易腿直接以原
GenStructure名称作为Struct值,完全符合示例输出要求
扩展思路
如果需要更灵活的匹配规则,可以尝试:
- 给结构化组合设置优先级,按优先级顺序匹配(比如优先匹配高频组合)
- 增加模糊匹配逻辑:比如剩余1条腿时,根据业务规则合并到最近的组合
- 预处理数据质量问题:过滤掉数量小于阈值的异常交易腿
内容的提问来源于stack exchange,提问作者keynesiancross
相关产品推荐
相关产品推荐

