Нормализация гистограммы с помощью matplotlib
Я хочу построить гистограмму с помощью Matplotlib, но я хотел бы, чтобы значения ячеек представляли процент от общего числа наблюдений. MWE будет выглядеть так:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import matplotlib.ticker as tck
import seaborn as sns
import numpy
sns.set(style='dark')
imagen2 = plt.figure(1, figsize=(5, 2))
imagen2.suptitle('StackOverflow Matplotlib histogram demo')
luminance = numpy.random.randn(1000, 1000)
# "Luminance" should range from 0.0...1.0 so we normalize it
luminance = (luminance - luminance.min())/(luminance.max() - luminance.min())
top_left = plt.subplot(121)
top_left.imshow(luminance)
bottom_left = plt.subplot(122)
sns.distplot(luminance.flatten(), kde_kws={"cumulative": True})
# plt.savefig("stackoverflow.pdf", dpi=300)
plt.tight_layout(rect=(0, 0, 1, 0.95))
plt.show()
CDF здесь в порядке (диапазон: [0, 1]), но полученная гистограмма не соответствует моим ожиданиям:
Почему результаты гистограммы находятся в диапазоне [0, 4]? Есть ли способ исправить это?
2 ответов:
Если вы хотите видеть гистограмму на второй оси y (лучше визуально), добавьтеlf = luminance.flatten() sns.kdeplot(lf, cumulative=True) sns.distplot(lf, kde=False, hist_kws={'weights': numpy.full(len(lf), 1/len(lf))})ax=bottom_left.twinx()кsns.distplot:
Что ты думаешь, что хочешь
Вот как построить гистограмму таким образом, чтобы ячейки суммировались в 1:
import matplotlib.pyplot as plt import matplotlib.ticker as tck import seaborn as sns import numpy as np sns.set(style='dark') imagen2 = plt.figure(1, figsize=(5, 2)) imagen2.suptitle('StackOverflow Matplotlib histogram demo') luminance = numpy.random.randn(1000, 1000) # "Luminance" should range from 0.0...1.0 so we normalize it luminance = (luminance - luminance.min())/(luminance.max() - luminance.min()) # get the histogram values heights,edges = np.histogram(luminance.flat, bins=30) binCenters = (edges[:-1] + edges[1:])/2 # norm the heights heights = heights/heights.sum() # get the cdf cdf = heights.cumsum() left = plt.subplot(121) left.imshow(luminance) right = plt.subplot(122) right.plot(binCenters, cdf, binCenters, heights) # plt.savefig("stackoverflow.pdf", dpi=300) plt.tight_layout(rect=(0, 0, 1, 0.95)) plt.show() # confirm that the hist vals sum to 1 print('heights sum: %.2f' % heights.sum())Вывод:
heights sum: 1.00Фактический ответ
На самом деле это очень просто. Просто сделай
sns.distplot(luminance.flatten(), kde_kws={"cumulative": True}, norm_hist=True)Вот что я получаю, когда запускаю ваш скрипт с вышеуказанной модификацией:
Неожиданный поворот!
Таким образом, получается, что ваша гистограмма была нормализована все время, в соответствии с формальным идентичность:
На простом (er) английском языке общей практикой является нормирование непрерывно оцениваемых гистограмм (то есть их наблюдения могут быть выражены как число с плавающей точкой) в терминах их плотности. Таким образом, в этом случае сумма ширины ячейки, умноженная на высоту ячейки, будет равна 1,0, как вы можете видеть, запустив эту упрощенную версию вашего скрипта:import matplotlib.pyplot as plt import matplotlib.ticker as tck import numpy as np imagen2 = plt.figure(1, figsize=(4,3)) imagen2.suptitle('StackOverflow Matplotlib histogram demo') luminance = numpy.random.randn(1000, 1000) luminance = (luminance - luminance.min())/(luminance.max() - luminance.min()) heights,edges,patches = plt.hist(luminance.ravel(), density=True, bins=30) widths = edges[1:] - edges[:-1] totalWeight = (heights*widths).sum() # plt.savefig("stackoverflow.pdf", dpi=300) plt.tight_layout(rect=(0, 0, 1, 0.95)) plt.show() print(totalWeight)И
totalWeightдействительно будут точно равны1.0, плюс-минус небольшая ошибка округления.






Comments