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

如何基于多条件批量计算Pandas DataFrame压力情景的实际数值?

批量转换压力情景数据至基准单位的Pandas实现方案

场景描述

需要基于匹配条件对DataFrame中的压力情景行执行乘法运算,将百分比差值转换为与基准情景一致的绝对数值单位。

DataFrame示例(从xlsx文件导入)

Model    Scenario    Region    Variable    Unit    Year1    Year2    ...    Year50
  1        Base        1         GDP      M USD     10       15               20  
  1        Base        2         GDP      M USD     30       35               50  
  1        Base        3         GDP      M USD     20       75               80  
  1        Stress 1    1         GDP      % diff    0.48    0.11             0.31  
  1        Stress 1    2         GDP      % diff    0.12    0.33             0.89  
  1        Stress 1    3         GDP      % diff    0.76    0.54             0.08  
  1        Stress 2    1         GDP      % diff    0.37    0.94             0.13  
  1        Stress 2    2         GDP      % diff    0.73    0.76             0.35  
  1        Stress 2    3         GDP      % diff    0.15    0.45             0.37  
  1        Stress 3    1         GDP      % diff    0.49    0.14             0.37  
  1        Stress 3    2         GDP      % diff    0.14    0.73             0.94  
  1        Stress 3    3         GDP      % diff    0.96    0.26             0.85  

核心规则

  • 压力情景的百分比差值对应相同模型、区域、变量的基准情景数值,计算逻辑为:基准值 * (1 + 压力差值)
  • 原始DataFrame包含多组模型、情景、区域和变量,结构统一(所有模型的情景集合一致,所有情景的区域集合一致等)

目标

将所有压力情景行的Year列数值转换为基准情景的单位,同时保留其他列的信息(如Scenario、Unit等)。

计算示例

公式示意

Model    Scenario    ...    Year1          Year2    ...     Year50
  1      Stress 1        10*(1+0.48)    15*(1+0.11)        20*(1+0.31) 

输出结果

Model    Scenario    ...    Year1          Year2    ...     Year50
  1      Stress 1           14.8           16.65             26.2 

已尝试方案及问题

曾用df.loc硬编码匹配条件计算,但存在两个核心问题:

test_df.loc[((test_df['Model'] == '1') & (test_df['Scenario'] == 'Stress1') & (test_df['Region'] == "1") & (test_df['Variable'] == 'GDP'))] = test_df.loc[((test_df['Model'] == '1') & (test_df['Scenario'] == 'Base') & (test_df['Region'] == "1") & (test_df['Variable'] == 'GDP'))] * (1 + test_df.loc[((test_df['Model'] == '1') & (test_df['Scenario'] == 'Stress1') & (test_df['Region'] == "1") & (test_df['Variable'] == 'GDP'))])
  1. 无法精准控制仅修改Year列,会覆盖Scenario、Unit等非数值列
  2. 需要为每个模型/情景/区域/变量组合单独编写代码,无法批量处理

最优解决方案

利用Pandas的分组合并功能,一次性匹配所有对应基准值并计算,无需硬编码条件:

步骤1:拆分基准与压力数据

先将基准情景数据单独提取,作为匹配的参照表:

# 提取基准情景数据,保留匹配键和Year列
base_df = test_df[test_df['Scenario'] == 'Base'].copy()
# 重命名Year列为前缀,避免合并后列名冲突
year_cols = [col for col in test_df.columns if col.startswith('Year')]
base_df = base_df.rename(columns={col: f'Base_{col}' for col in year_cols})

步骤2:合并基准与压力数据

以Model、Region、Variable为匹配键,将基准数据合并到压力情景行:

# 筛选压力情景数据
stress_df = test_df[test_df['Scenario'] != 'Base'].copy()
# 合并基准数据
merged_df = stress_df.merge(
    base_df[['Model', 'Region', 'Variable'] + [f'Base_{col}' for col in year_cols]],
    on=['Model', 'Region', 'Variable'],
    how='left'
)

步骤3:执行计算并更新Year列

遍历Year列,用基准值乘以(1+压力差值),同时更新Unit列为基准单位:

# 遍历所有Year列计算
for col in year_cols:
    merged_df[col] = merged_df[f'Base_{col}'] * (1 + merged_df[col])

# 按变量匹配对应基准单位(适配多变量场景)
merged_df['Unit'] = merged_df.apply(
    lambda row: base_df[(base_df['Variable'] == row['Variable']) & (base_df['Model'] == row['Model'])]['Unit'].iloc[0],
    axis=1
)

# 清理临时列
merged_df = merged_df.drop(columns=[f'Base_{col}' for col in year_cols])

步骤4:合并基准与转换后的压力数据(可选)

如果需要保留原始基准行,将转换后的压力数据与原基准数据合并:

final_df = pd.concat([test_df[test_df['Scenario'] == 'Base'], merged_df], ignore_index=True)

完整代码示例

import pandas as pd

# 导入原始数据
test_df = pd.read_excel('your_file.xlsx')

# 提取基准情景并处理列名
year_cols = [col for col in test_df.columns if col.startswith('Year')]
base_df = test_df[test_df['Scenario'] == 'Base'].copy()
base_df = base_df.rename(columns={col: f'Base_{col}' for col in year_cols})

# 处理压力情景数据
stress_df = test_df[test_df['Scenario'] != 'Base'].copy()
merged_df = stress_df.merge(
    base_df[['Model', 'Region', 'Variable'] + [f'Base_{col}' for col in year_cols]],
    on=['Model', 'Region', 'Variable'],
    how='left'
)

# 计算转换后数值
for col in year_cols:
    merged_df[col] = merged_df[f'Base_{col}'] * (1 + merged_df[col])

# 更新单位并清理临时列
merged_df['Unit'] = merged_df.apply(
    lambda row: base_df[(base_df['Variable'] == row['Variable']) & (base_df['Model'] == row['Model'])]['Unit'].iloc[0],
    axis=1
)
merged_df = merged_df.drop(columns=[f'Base_{col}' for col in year_cols])

# 合并基准与结果
final_df = pd.concat([test_df[test_df['Scenario'] == 'Base'], merged_df], ignore_index=True)

方案优势

  • 无需硬编码任何模型/情景/区域/变量的匹配条件,自动适配所有分组
  • 精准控制仅修改Year列,保留其他列的原始信息
  • 代码简洁高效,适配大规模DataFrame处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 09:05:40