102 votes

Comment puis-je tracer un histogramme de sorte que la somme des hauteurs des barres soit égale à 1 en matplotlib ?

J'aimerais tracer un histogramme normalisé à partir d'un vecteur en utilisant matplotlib. J'ai essayé ce qui suit :

plt.hist(myarray, normed=True)

ainsi que :

plt.hist(myarray, normed=1)

mais aucune des deux options ne produit un axe y de [0, 1] tel que la somme des hauteurs des barres de l'histogramme soit égale à 1. J'aimerais produire un tel histogramme - comment puis-je le faire ?

7 votes

Je sais que c'est vieux, mais pour référence future & toute personne qui visite cette page, ce type d'axe est appelé un axe de "densité de probabilité" !

231voto

Carsten König Points 2959

Si vous voulez que la somme de toutes les barres soit égale à l'unité, pondérez chaque case par le nombre total de valeurs :

weights = np.ones_like(myarray) / len(myarray)
plt.hist(myarray, weights=weights)

J'espère que cela vous aidera, bien que le fil de discussion soit assez ancien...

Note pour Python 2.x : ajouter un casting à float() pour l'un des opérateurs de la division, car sinon vous vous retrouveriez avec des zéros en raison de la division entière

8 votes

Excellente réponse. Notez que si myarray est un fichier python array_like plutôt que d'un tableau numpy, vous devrez convertir len(myarray) a float .

3 votes

De même, si myarray est multidimensionnel et que vous n'utilisez qu'une seule dimension, comme myarray[0, :], vous pouvez remplacer len(myarray) par np.size(myarray[0, :]) et cela fonctionnera de la même manière. (Sinon, il est dit que l'objet n'est pas appelable).

52voto

dtlussier Points 965

Il serait plus utile que vous posiez un exemple fonctionnel (ou dans ce cas non fonctionnel) plus complet.

J'ai essayé ce qui suit :

import numpy as np
import matplotlib.pyplot as plt

x = np.random.randn(1000)

fig = plt.figure()
ax = fig.add_subplot(111)
n, bins, rectangles = ax.hist(x, 50, density=True)
fig.canvas.draw()
plt.show()

Cela produira en effet un histogramme en forme de barre avec un axe y qui va de [0,1] .

En outre, conformément à la hist la documentation (c'est-à-dire ax.hist? de ipython ), je pense que la somme est bonne aussi :

*normed*:
If *True*, the first element of the return tuple will
be the counts normalized to form a probability density, i.e.,
``n/(len(x)*dbin)``.  In a probability density, the integral of
the histogram should be 1; you can verify that with a
trapezoidal integration of the probability density function::

    pdf, bins, patches = ax.hist(...)
    print np.sum(pdf * np.diff(bins))

J'essaie de le faire après les commandes ci-dessus :

np.sum(n * np.diff(bins))

J'obtiens une valeur de retour de 1.0 comme prévu. N'oubliez pas que normed=True ne signifie pas que la somme des valeurs à chaque barre sera égale à l'unité, mais plutôt que l'intégrale sur les barres sera égale à l'unité. Dans mon cas np.sum(n) rendu approx. 7.2767 .

3 votes

Ouais, c'est un graphique de densité de probabilité, je pense qu'il veut un graphique de masse de probabilité.

23voto

Killer Points 397

Je sais que cette réponse est trop tardive puisque la question date de 2010 mais je suis tombé sur cette question car j'étais moi-même confronté à un problème similaire. Comme indiqué dans la réponse, normed=True signifie que la surface totale sous l'histogramme est égale à 1 mais que la somme des hauteurs n'est pas égale à 1. Cependant, je voulais, pour faciliter l'interprétation physique d'un histogramme, en faire un avec la somme des hauteurs égale à 1.

J'ai trouvé un indice dans la question suivante - Python : Histogramme avec surface normalisée à quelque chose d'autre que 1

Mais je n'ai pas réussi à trouver un moyen de faire en sorte que les barres imitent la fonction histtype="step" hist(). Cela m'a amené à me tourner vers : Matplotlib - Histogramme en escalier avec des données déjà binées

Si la communauté le trouve acceptable, je voudrais proposer une solution qui synthétise les idées des deux postes ci-dessus.

import matplotlib.pyplot as plt

# Let X be the array whose histogram needs to be plotted.
nx, xbins, ptchs = plt.hist(X, bins=20)
plt.clf() # Get rid of this histogram since not the one we want.

nx_frac = nx/float(len(nx)) # Each bin divided by total number of objects.
width = xbins[1] - xbins[0] # Width of each bin.
x = np.ravel(zip(xbins[:-1], xbins[:-1]+width))
y = np.ravel(zip(nx_frac,nx_frac))

plt.plot(x,y,linestyle="dashed",label="MyLabel")
#... Further formatting.

Cela a fonctionné à merveille pour moi, bien que dans certains cas, j'ai remarqué que la "barre" la plus à gauche ou la "barre" la plus à droite de l'histogramme ne se ferme pas en touchant le point le plus bas de l'axe Y. Dans ce cas, l'ajout d'un élément 0 au début ou à la fin de y a permis d'obtenir le résultat nécessaire. Dans ce cas, l'ajout d'un élément 0 au début ou à la fin de l'axe y a permis d'obtenir le résultat souhaité.

J'ai juste pensé que je devais partager mon expérience. Merci.

0 votes

Je pense que vous avez besoin de normed=True également dans plt.hist. En outre, dans Python 3, vous devez utiliser list(zip(...)).

15voto

Yuri Brovman Points 853

Voici une autre solution simple utilisant np.histogram() méthode.

myarray = np.random.random(100)
results, edges = np.histogram(myarray, normed=True)
binWidth = edges[1] - edges[0]
plt.bar(edges[:-1], results*binWidth, binWidth)

Vous pouvez en effet vérifier que le total est égal à 1 avec :

> print sum(results*binWidth)
1.0

9voto

Trenton_M Points 734
  • La solution la plus simple consiste à utiliser seaborn.histplot o seaborn.displot avec kind='hist' et préciser stat='probability'
    • probabilité : ou proportion normaliser pour que la somme des hauteurs des barres soit égale à 1.
    • densité : normaliser de telle sorte que la surface totale de l'histogramme soit égale à 1
    • data : pandas.DataFrame , numpy.ndarray , cartographie ou séquence
  • seaborn est une API de haut niveau pour matplotlib
  • Testé dans python 3.8.12 , matplotlib 3.4.3 , seaborn 0.11.2

Importations et données

import seaborn as sns
import matplotlib.pyplot as plt

# load data
df = sns.load_dataset('penguins')

sns.histplot

  • tracé au niveau des axes

    create figure and axes

    fig, ax = plt.subplots(figsize=(6, 5))

    p = sns.histplot(data=df, x='flipper_length_mm', stat='probability', ax=ax)

enter image description here

sns.displot

  • tracé au niveau des figures

    p = sns.displot(data=df, x='flipper_length_mm', stat='probability', height=4, aspect=1.5)

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X