偏移直方图分箱计算的微小偏差问题排查
问题分析与修正方案
偏差根源
- 分箱边界计算错误:原代码中
bins[i] = hedge_p50 - (50 - i) * bin_size - hedge_p50/200的偏移逻辑混乱,导致分箱范围未准确对应1%步长,且左边界未合理设置。 - 直方图参数误用:使用
density=True返回的是概率密度而非样本计数,后续加权求和逻辑不符合还原原始p50的需求。 - 样本覆盖不全:部分样本可能落在定义的分箱之外,导致求和时遗漏。
修正步骤
- 重构分箱逻辑:生成从对冲后p50的-50%到+50%的1%步长分箱,确保覆盖所有样本。
- 改用样本计数计算:用
np.histogram默认的density=False获取箱内样本数,以箱中值乘以样本数得到箱总贡献。 - 非边缘箱归一化:将非边缘箱的总和缩放至匹配目标p50(当年退化后的p50值),边缘箱保持原始计算结果。
- 优化样本生成:用向量运算替代嵌套循环,提升代码效率。
修正后的代码
import numpy as np import pandas as pd from numpy.random import normal # --- 定义第一年p50分布 --- p50 = 100000 hedge = -0.10 # 百分比 uncertainty = 0 # 百分比 sd = p50 * uncertainty / 100 first_year = normal(p50, sd, 10000) # --- 生成多年退化样本(向量运算优化) --- years = 10 deg = 0.1 deg_factors = (1 - deg / 100) ** np.arange(years) yearly_dist = first_year[np.newaxis, :] * deg_factors[:, np.newaxis] # --- 生成基于对冲p50的1%步长分箱 --- hedge_p50 = p50 * (1 + hedge / 100) bin_size_pct = 0.01 # 生成从-50%到+50%的分箱边界,共101个点 bins = hedge_p50 * (1 + np.arange(-50, 51) * bin_size_pct) bins[-1] = np.inf # 最右边界设为无穷大,确保覆盖所有样本 # --- 计算每年各分箱的总贡献并修正偏差 --- yearly_df = pd.DataFrame() for i in range(years): deg_value = deg_factors[i] target_p50 = p50 * deg_value deg_bins = bins * deg_value deg_bins[-1] = np.inf # 获取每个箱的样本计数 counts, bin_edges = np.histogram(yearly_dist[i], bins=deg_bins) # 计算箱中值 bin_mids = (bin_edges[:-1] + bin_edges[1:]) / 2 # 计算箱总贡献 bin_totals = counts * bin_mids # 修正非边缘箱的总和至目标p50 non_edge_mask = np.arange(len(bin_totals))[1:-1] non_edge_sum = bin_totals[non_edge_mask].sum() if non_edge_sum != 0: scale_factor = target_p50 / non_edge_sum bin_totals[non_edge_mask] *= scale_factor yearly_df[i] = bin_totals # 验证第一年总和 print(f"第一年总和:{yearly_df[0].sum()}")
验证结果
修正后,yearly_df[0].sum()会精确等于100000(当uncertainty=0时),后续年份的总和将准确匹配p50 * (1 - deg/100)**i。
内容的提问来源于stack exchange,提问作者C. Nielsen
相关产品推荐
相关产品推荐

