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

matplotlib绘制直方图如何设置柱高为区间概率且柱高总和为1

问题描述
  • 核心需求:基于Pandas数据框存储的数据绘制直方图,要求每个分箱(bin)的y轴取值等于该区间内的事件发生概率(即分箱内样本数/总样本数)
  • 问题背景:matplotlib.pyplot.hist的density=True参数计算逻辑为分箱内计数/(总计数*分箱宽度),返回值为单位区间的概率密度;当分箱宽度不等于1时,y轴取值并非分箱对应的事件发生概率。本次需求分箱宽度为10个单位,因此出现数值不匹配问题。
  • 已知该实现下直方图积分结果不等于1(本例积分值为10),为预期效果,无需调整为密度归一化逻辑。
  • 测试数据集生成代码:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from random import seed
from random import randint

data = pd.DataFrame(columns=['Col1'])

i = 0
while i < 49500:
    data.loc[len(data.index)] = [0]
    i += 1

seed(1)
j = 0
while j < 500:
    data.loc[len(data.index)] = [randint(1,500)]
    j += 1
  • 原有绘图代码:
plt.figure(2)
fig2, ax2 = plt.subplots()
ax2.hist(data['Col1'], range=(0.0, 500.0), bins=50, label='50000 numbers\n in 10 unit bins', density=True)
plt.title('Probability Density of Some Numbers from 0 to 500', wrap=True)
plt.legend(loc='upper right')
plt.yscale('log')
plt.xticks()
plt.minorticks_on()
plt.ylabel('Probability')
plt.xlabel('Number')
plt.savefig('randnum.png')
  • 异常表现:0-10分箱的数据占比约99%,但对应y轴概率值仅显示为0.1,不符合预期。
  • 期望实现路径:
    1. 修改直方图的归一化基准值,直接输出分箱概率
    2. 支持直接将直方图的y轴取值乘以自定义数值调整
解决方案

方案1:通过权重参数修改归一化基准(推荐)

不需要启用density参数,直接用weights参数指定每个样本的权重为1/总样本量,此时hist计算分箱高度时会自动对分箱内的权重求和,结果正好是分箱内样本数/总样本数,也就是需要的分箱事件概率。
替换原有hist调用部分即可,完整代码如下:

total_samples = len(data['Col1'])
plt.figure(2)
fig2, ax2 = plt.subplots()
ax2.hist(
    data['Col1'], 
    range=(0.0, 500.0), 
    bins=50, 
    weights=np.ones(total_samples) / total_samples,
    label='50000 numbers\n in 10 unit bins'
)
plt.title('Probability of Some Numbers from 0 to 500', wrap=True)
plt.legend(loc='upper right')
plt.yscale('log')
plt.minorticks_on()
plt.ylabel('Probability')
plt.xlabel('Number')
plt.savefig('randnum_prob.png')

运行后0-10分箱的y值会正确显示为0.99,和实际样本占比一致。

方案2:缩放density模式下的y轴取值

如果需要保留density=True的计算逻辑,可以直接对返回的分箱高度做缩放:概率密度乘以分箱宽度,结果就是对应分箱的事件概率。
ax.hist会返回三个对象:分箱高度数组、分箱边界数组、分箱对应的图形patch列表,遍历patch修改高度即可,代码如下:

plt.figure(2)
fig2, ax2 = plt.subplots()
n, bins, patches = ax2.hist(
    data['Col1'], 
    range=(0.0, 500.0), 
    bins=50, 
    density=True,
    label='50000 numbers\n in 10 unit bins'
)
bin_width = bins[1] - bins[0]
# 按分箱宽度缩放高度,将概率密度转换为分箱概率,也可替换为任意自定义缩放系数
for p, h in zip(patches, n):
    p.set_height(h * bin_width)
# 刷新轴范围适配修改后的柱高
ax2.autoscale_view()

plt.title('Probability of Some Numbers from 0 to 500', wrap=True)
plt.legend(loc='upper right')
plt.yscale('log')
plt.minorticks_on()
plt.ylabel('Probability')
plt.xlabel('Number')
plt.savefig('randnum_prob_scaled.png')

两种方案输出效果完全一致,方案2支持传入任意自定义系数缩放y值,适配更多自定义场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:09:47