如何用 Python 迭代生成单变量敏感性分析配置(P10/P90)

12次阅读

如何用 Python 迭代生成单变量敏感性分析配置(P10/P90)

本文介绍如何利用 `itertools.product` 和动态字段替换,高效生成单变量扰动的敏感性分析配置列表,支持任意数量输入参数,避免硬编码,适用于模型输入的 p10/p90 单因素敏感性分析。

在进行模型敏感性分析时,一个常见且严谨的做法是:每次仅改变一个输入变量(如设为 P10 或 P90),其余变量保持基准值(P50)不变。这种“单变量扰动”策略能清晰归因输出变化,但手动构造配置极易出错,尤其当输入维度升高(如 10+ 个参数)时。

python 提供了优雅的解决方案——结合 itertools.product 与命名元组(namedtuple)的字段反射能力,可全自动、可扩展地生成全部所需配置。

首先,定义你的敏感性用例结构(如题中所示):

from collections import namedtuple from itertools import product  Sensitivity_Case = namedtuple('Sensitivity_Case', ['a', 'b', 'c'])  p50 = Sensitivity_Case(5, 50, 'medium') p10 = Sensitivity_Case(1, 5, 'low') p90 = Sensitivity_Case(10, 500, 'high')

关键在于:我们不预先写死 ‘a.p10’ 这类字符串,而是将每个字段的三种取值(P10/P50/P90)组织为独立列表,并明确指定“仅允许一个字段偏离 P50”。

立即学习Python免费学习笔记(深入)”;

以下函数 generate_sensitivity_configs 实现该逻辑:

def generate_sensitivity_configs(base_case, cases: list):     """     生成单变量敏感性配置列表。      Args:         base_case: 基准用例(如 p50),用于提供字段名和默认值         cases: 包含多个 Sensitivity_Case 实例的列表,顺序应为 [p10, p50, p90]      Returns:         list of tuples: 每个 tuple 是一个完整配置(对应模型一次输入)     """     field_names = base_case._fields     n_fields = len(field_names)      # 对每个字段,提取其在所有 cases 中的取值(如 a: [p10.a, p50.a, p90.a])     field_values = [         [case._asdict()[field] for case in cases]         for field in field_names     ]      configs = []     # 遍历每个字段索引(即哪个字段被扰动)     for i in range(n_fields):         # 当前扰动字段:取 P10 和 P90(跳过 P50)         for perturb_val in [field_values[i][0], field_values[i][2]]:  # p10, p90             # 其余字段固定为 P50(即 cases[1])             config_vals = []             for j, field in enumerate(field_names):                 if j == i:                     config_vals.append(perturb_val)                 else:                     config_vals.append(cases[1]._asdict()[field])             configs.append(tuple(config_vals))      return configs  # 使用示例 configs = generate_sensitivity_configs(p50, [p10, p50, p90]) for i, cfg in enumerate(configs, 1):     print(f"config{i} = {cfg}")

输出结果完全匹配需求:

config1 = (1, 50, 'medium')     # a.p10, b.p50, c.p50 config2 = (10, 50, 'medium')    # a.p90, b.p50, c.p50 config3 = (5, 5, 'medium')      # a.p50, b.p10, c.p50 config4 = (5, 500, 'medium')    # a.p50, b.p90, c.p50 config5 = (5, 50, 'low')        # a.p50, b.p50, c.p10 config6 = (5, 50, 'high')       # a.p50, b.p50, c.p90

优势说明

  • 自动适配字段数:无论 Sensitivity_Case 含 3 个还是 100 个字段,逻辑不变;
  • 清晰分离关注点:cases 列表定义取值集(P10/P50/P90),函数负责组合逻辑;
  • 类型安全 & 可读性强:输出为 tuple,可直接解包传入模型函数(如 model(*cfg));
  • 无冗余配置:不生成全 P50 基准配置(若需,可单独添加 configs.append(tuple(p50)))。

⚠️ 注意事项

  • 确保 cases 参数中 p50 严格位于索引 1(即 [p10, p50, p90]),否则 cases[1] 将取错基准值;
  • 若未来需支持更多分位点(如 P5/P25/P75/P95),只需扩展 cases 列表并调整内层循环逻辑;
  • 如需返回 namedtuple 而非 tuple,可在最后用 Sensitivity_Case(*cfg) 封装

通过此方法,你彻底告别手动枚举,以不到 20 行核心代码,构建出健壮、可维护、可扩展的敏感性配置生成器——这才是 Python “优雅解决实际问题”的典型范式。

text=ZqhQzanResources