基于Matplotlib和Numpy的数值字典分析与泊松误差传递
我来帮你一步步解决这个问题,从区间中心值的修正到泊松误差棒的计算,再到两幅图的实现,咱们逐个击破:
1. 先搞定区间中心值的计算
你之前的中心值代码失效,大概率是用了算术平均处理对数空间的区间——但np.logspace生成的是对数均匀分布的区间,算术平均会被大数值拉偏,正确的做法是用几何平均(对数中心),这样才能代表区间的典型位置:
import numpy as np import collections from matplotlib import pyplot as plt # 生成区间边界 bins = np.logspace(8.20, 12.20, 11) # 计算每个区间的几何平均中心(对数空间的中点) bin_centers = np.sqrt(bins[:-1] * bins[1:]) # 等价写法:10 ** ((np.log10(bins[:-1]) + np.log10(bins[1:])) / 2)
这个中心值会完美贴合对数坐标轴的区间中点,解决你之前的中心值失效问题。
2. 定义核心计算函数
先把权重和平均值贡献的逻辑封装成函数,方便后续调用:
def calculate_weight(item_list): """根据列表元素数量返回对应权重""" n = len(item_list) if n == 3: return 1.0 elif n == 2: return 2/3 elif n == 1: return 1/3 return 0.0 # 空列表不贡献 def calculate_avg_contribution(item_list): """返回列表元素的平均值,空列表返回0""" if not item_list: return 0.0 return np.mean(item_list)
3. 计算区间统计量与双重泊松误差
这里要处理单个键的不确定性和区间总和的不确定性,两者叠加得到最终的误差棒:
# 假设你的字典是 data_dict = collections.defaultdict(list) # 初始化存储变量 fig1_weight_totals = [] fig1_weight_errors = [] fig2_avg_sums = [] fig2_avg_errors = [] # 遍历每个区间 for low, high in zip(bins[:-1], bins[1:]): # 筛选当前区间内的所有键 keys_in_bin = [k for k in data_dict.keys() if low <= k < high] # === 图1:加权总数与误差 === weights = [calculate_weight(data_dict[k]) for k in keys_in_bin] total_weight = sum(weights) fig1_weight_totals.append(total_weight) # 双重泊松误差计算: # 每个键的权重对应n/3(n是列表元素数),n的泊松误差是sqrt(n),因此权重误差为sqrt(n)/3 = sqrt(3w)/3 = sqrt(w/3) # 区间总和的误差是各键误差的平方和开根号(独立误差方差相加) weight_errors = [np.sqrt(len(data_dict[k])) / 3 for k in keys_in_bin] total_weight_error = np.sqrt(sum([e**2 for e in weight_errors])) fig1_weight_errors.append(total_weight_error) # === 图2:平均值之和与误差 === avg_contribs = [calculate_avg_contribution(data_dict[k]) for k in keys_in_bin] total_avg = sum(avg_contribs) fig2_avg_sums.append(total_avg) # 平均值的误差是样本标准误(std/sqrt(n)),区间总和误差同样是各标准误的平方和开根号 avg_errors = [] for k in keys_in_bin: lst = data_dict[k] n = len(lst) if n == 0: avg_errors.append(0.0) continue sample_std = np.std(lst, ddof=1) # 样本标准差 standard_error = sample_std / np.sqrt(n) avg_errors.append(standard_error) total_avg_error = np.sqrt(sum([e**2 for e in avg_errors])) fig2_avg_errors.append(total_avg_error) # 图2归一化到总和为1,误差同步按比例缩放 total_fig2_sum = sum(fig2_avg_sums) fig2_avg_normalized = [x / total_fig2_sum for x in fig2_avg_sums] fig2_errors_normalized = [e / total_fig2_sum for e in fig2_avg_errors]
4. 绘制两幅带误差棒的图
最后用plt.errorbar绘制带泊松误差棒的图,注意x轴用对数刻度:
图1:区间加权总数
plt.figure(figsize=(10, 5)) plt.errorbar(bin_centers, fig1_weight_totals, yerr=fig1_weight_errors, fmt='o-', capsize=5, color='tab:blue') plt.xscale('log') plt.xlabel('Key Value (Log Scale)') plt.ylabel('Weighted Total') plt.title('Weighted Count per Log-Space Interval') plt.grid(True, which='both', linestyle='--', alpha=0.7) plt.show()
图2:归一化平均值之和
plt.figure(figsize=(10, 5)) plt.errorbar(bin_centers, fig2_avg_normalized, yerr=fig2_errors_normalized, fmt='s-', capsize=5, color='tab:orange') plt.xscale('log') plt.xlabel('Key Value (Log Scale)') plt.ylabel('Normalized Average Sum') plt.title('Normalized Average Contribution per Log-Space Interval') plt.grid(True, which='both', linestyle='--', alpha=0.7) plt.ylim(0, max(fig2_avg_normalized) + max(fig2_errors_normalized)*1.2) # 优化y轴范围 plt.show()
关键问题说明
为什么用几何平均做中心值?
因为你的区间是对数均匀分布的,算术平均会偏向大数值,几何平均(对数中点)才是区间的真实“中心”,能让x轴的点完美对应区间的中间位置。双重泊松误差怎么理解?
- 单个键的误差:列表元素数n本身服从泊松分布,误差为
sqrt(n),权重/平均值的误差由此推导而来; - 区间总和的误差:多个独立误差的方差相加,再开根号得到总误差,这是统计上合并独立不确定性的标准方法。
- 单个键的误差:列表元素数n本身服从泊松分布,误差为
内容的提问来源于stack exchange,提问作者Rebel




