Tracer deux histogrammes sur un seul graphique avec matplotlib


234

J'ai créé un tracé d'histogramme en utilisant les données d'un fichier et aucun problème. Maintenant, je voulais superposer les données d'un autre fichier dans le même histogramme, donc je fais quelque chose comme ça

n,bins,patchs = ax.hist(mydata1,100)
n,bins,patchs = ax.hist(mydata2,100)

mais le problème est que pour chaque intervalle, seule la barre avec la valeur la plus élevée apparaît, et l'autre est masquée. Je me demande comment pourrais-je tracer les deux histogrammes en même temps avec des couleurs différentes.

Réponses:


418

Voici un exemple de travail:

import random
import numpy
from matplotlib import pyplot

x = [random.gauss(3,1) for _ in range(400)]
y = [random.gauss(4,2) for _ in range(400)]

bins = numpy.linspace(-10, 10, 100)

pyplot.hist(x, bins, alpha=0.5, label='x')
pyplot.hist(y, bins, alpha=0.5, label='y')
pyplot.legend(loc='upper right')
pyplot.show()

entrez la description de l'image ici


1
Ne serait-ce pas une bonne idée de définir pyplot.hold(True)avant de comploter, juste au cas où?
JAB

2
Je ne sais pas si hold (True) est défini dans mes paramètres de configuration matplotlib ou pyplot se comporte comme ceci par défaut, mais pour moi, le code fonctionne tel quel. Le code est extrait d'une application plus grande qui ne pose aucun problème jusqu'à présent. Quoi qu'il en soit, bonne question que je me suis déjà posée lors de l'écriture du code
joaquin

@joaquin: comment pourrais-je spécifier que x soit bleu et y rouge?
amc

7
Quand j'ai reproduit l'intrigue avec la couleur edg des barres c'est Nonepar défaut. Si vous souhaitez la même conception que celle illustrée dans le graphique, vous pouvez définir le edgecolorparamètre dans les deux par exemple sur k(noir). La procédure est similaire pour la légende.
So S

2
Encore plus facile: pyplot.hist([x, y], bins, alpha=0.5, label=['x', 'y']).
Augustin

174

Les réponses acceptées donnent le code d'un histogramme avec des barres qui se chevauchent, mais si vous voulez que chaque barre soit côte à côte (comme je l'ai fait), essayez la variante ci-dessous:

import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-deep')

x = np.random.normal(1, 2, 5000)
y = np.random.normal(-1, 3, 2000)
bins = np.linspace(-10, 10, 30)

plt.hist([x, y], bins, label=['x', 'y'])
plt.legend(loc='upper right')
plt.show()

entrez la description de l'image ici

Référence: http://matplotlib.org/examples/statistics/histogram_demo_multihist.html

EDIT [16/03/2018]: mis à jour pour permettre le traçage de tableaux de différentes tailles, comme suggéré par @stochastic_zeitgeist


@GustavoBezerra, comment utiliser plt.histpour produire un fichier pdf pour chaque histogramme? J'ai chargé mes données en utilisant pandas.read_csvet le fichier a 36 colonnes et 100 lignes. Je voudrais donc 100 fichiers pdf.
Sigur

2
@Sigur C'est tout à fait hors sujet. Veuillez Google ou poser une nouvelle question. Cela semble être lié: stackoverflow.com/questions/11328958/…
Gustavo Bezerra

1
@stochastic_zeitgeist Je suis d'accord avec @pasbi. J'ai utilisé votre commentaire avec une trame de données pandas car j'avais besoin de poids différents en raison des nans. avec x=np.array(df.a)et y=np.array(df.b.dropna())il a fini par êtreplt.hist([x, y], weights=[np.ones_like(x)/len(x), np.ones_like(y)/len(y)])
grinsbaeckchen

1
Dans le cas où vos tailles d'échantillon sont radicalement différentes, vous voudrez peut-être tracer en utilisant deux axes pour mieux comparer les distributions. Voir ci - dessous .
Andrew

1
@ AgapeGal'lo Veuillez vous référer à la réponse d'Andrew.
Gustavo Bezerra

30

Dans le cas où vous avez des tailles d'échantillon différentes, il peut être difficile de comparer les distributions avec un seul axe y. Par exemple:

import numpy as np
import matplotlib.pyplot as plt

#makes the data
y1 = np.random.normal(-2, 2, 1000)
y2 = np.random.normal(2, 2, 5000)
colors = ['b','g']

#plots the histogram
fig, ax1 = plt.subplots()
ax1.hist([y1,y2],color=colors)
ax1.set_xlim(-10,10)
ax1.set_ylabel("Count")
plt.tight_layout()
plt.show()

hist_single_ax

Dans ce cas, vous pouvez tracer vos deux ensembles de données sur des axes différents. Pour ce faire, vous pouvez obtenir vos données d'histogramme à l'aide de matplotlib, effacer l'axe, puis les re-tracer sur deux axes distincts (en déplaçant les bords du bac pour qu'ils ne se chevauchent pas):

#sets up the axis and gets histogram data
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.hist([y1, y2], color=colors)
n, bins, patches = ax1.hist([y1,y2])
ax1.cla() #clear the axis

#plots the histogram data
width = (bins[1] - bins[0]) * 0.4
bins_shifted = bins + width
ax1.bar(bins[:-1], n[0], width, align='edge', color=colors[0])
ax2.bar(bins_shifted[:-1], n[1], width, align='edge', color=colors[1])

#finishes the plot
ax1.set_ylabel("Count", color=colors[0])
ax2.set_ylabel("Count", color=colors[1])
ax1.tick_params('y', colors=colors[0])
ax2.tick_params('y', colors=colors[1])
plt.tight_layout()
plt.show()

hist_twin_ax


1
Ceci est une belle réponse brève, sauf que vous devez également ajouter comment centrer les barres sur chaque étiquette de tick
Odisseo

12

Pour compléter la réponse de Gustavo Bezerra :

Si vous voulez que chaque histogramme soit normalisé ( normedpour mpl <= 2.1 et densitypour mpl> = 3.1 ) que vous ne pouvez pas simplement utiliser normed/density=True, vous devez définir les poids pour chaque valeur à la place:

import numpy as np
import matplotlib.pyplot as plt

x = np.random.normal(1, 2, 5000)
y = np.random.normal(-1, 3, 2000)
x_w = np.empty(x.shape)
x_w.fill(1/x.shape[0])
y_w = np.empty(y.shape)
y_w.fill(1/y.shape[0])
bins = np.linspace(-10, 10, 30)

plt.hist([x, y], bins, weights=[x_w, y_w], label=['x', 'y'])
plt.legend(loc='upper right')
plt.show()

entrez la description de l'image ici

À titre de comparaison, les mêmes xet yvecteurs avec des poids par défaut et density=True:

entrez la description de l'image ici


9

Vous devez utiliser binsles valeurs renvoyées par hist:

import numpy as np
import matplotlib.pyplot as plt

foo = np.random.normal(loc=1, size=100) # a normal distribution
bar = np.random.normal(loc=-1, size=10000) # a normal distribution

_, bins, _ = plt.hist(foo, bins=50, range=[-6, 6], normed=True)
_ = plt.hist(bar, bins=bins, alpha=0.5, normed=True)

Deux histogrammes matplotlib avec le même binning


7

Voici une méthode simple pour tracer deux histogrammes, avec leurs barres côte à côte, sur le même tracé lorsque les données ont des tailles différentes:

def plotHistogram(p, o):
    """
    p and o are iterables with the values you want to 
    plot the histogram of
    """
    plt.hist([p, o], color=['g','r'], alpha=0.8, bins=50)
    plt.show()


2

Juste au cas où vous auriez des pandas ( import pandas as pd) ou êtes d'accord pour l'utiliser:

test = pd.DataFrame([[random.gauss(3,1) for _ in range(400)], 
                     [random.gauss(4,2) for _ in range(400)]])
plt.hist(test.values.T)
plt.show()

Je pense que l'utilisation de pandas ne fonctionnera pas si les histogrammes à comparer ont des tailles d'échantillon différentes. C'est aussi souvent le contexte dans lequel les histogrammes normalisés sont utilisés.
Solomon Vimal

2

Il y a une mise en garde lorsque vous souhaitez tracer l'histogramme à partir d'un tableau numpy 2D. Vous devez échanger les 2 axes.

import numpy as np
import matplotlib.pyplot as plt

data = np.random.normal(size=(2, 300))
# swapped_data.shape == (300, 2)
swapped_data = np.swapaxes(x, axis1=0, axis2=1)
plt.hist(swapped_data, bins=30, label=['x', 'y'])
plt.legend()
plt.show()

entrez la description de l'image ici


0

Cette question a déjà reçu une réponse, mais voulait ajouter une autre solution rapide / facile qui pourrait aider d'autres visiteurs à cette question.

import seasborn as sns 
sns.kdeplot(mydata1)
sns.kdeplot(mydata2)

Quelques exemples utiles sont ici pour la comparaison kde vs histogramme.


0

Inspiré par la réponse de Salomon, mais pour s'en tenir à la question, qui est liée à l'histogramme, une solution propre est:

sns.distplot(bar)
sns.distplot(foo)
plt.show()

Assurez-vous de tracer le plus grand en premier, sinon vous devrez définir plt.ylim (0,0.45) afin que l'histogramme plus grand ne soit pas coupé.


0

Également une option assez similaire à la réponse de joaquin:

import random
from matplotlib import pyplot

#random data
x = [random.gauss(3,1) for _ in range(400)]
y = [random.gauss(4,2) for _ in range(400)]

#plot both histograms(range from -10 to 10), bins set to 100
pyplot.hist([x,y], bins= 100, range=[-10,10], alpha=0.5, label=['x', 'y'])
#plot legend
pyplot.legend(loc='upper right')
#show it
pyplot.show()

Donne la sortie suivante:

entrez la description de l'image ici

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.