Trace dos histogramas en un solo gráfico con matplotlib

234

Creé un diagrama de histograma utilizando datos de un archivo y no hay problema. Ahora quería superponer datos de otro archivo en el mismo histograma, así que hago algo como esto

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

pero el problema es que para cada intervalo, solo aparece la barra con el valor más alto y el otro está oculto. Me pregunto cómo podría trazar ambos histogramas al mismo tiempo con diferentes colores.

Abre el camino
fuente

Respuestas:

418

Aquí tienes un ejemplo de trabajo:

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()

ingrese la descripción de la imagen aquí

joaquin
fuente
1
¿No sería una buena idea establecer pyplot.hold(True)antes de trazar, por si acaso?
JAB
2
No estoy seguro si hold (True) está configurado en mis parámetros de configuración de matplotlib o pyplot se comporta de esta manera por defecto, pero para mí el código funciona como está. El código se extrae de una aplicación más grande que no está dando ningún problema hasta ahora. De todos modos, buena pregunta que ya me hice al escribir el código
joaquin
@joaquin: ¿cómo podría especificar que x sea azul e y que sea rojo?
amc
77
Cuando reproduje la trama con el color del borde de las barras es Nonepor defecto. Si desea el mismo diseño que se muestra en el gráfico, puede establecer el edgecolorparámetro en ambos, por ejemplo, en k(negro). El procedimiento es similar para la leyenda.
Entonces, S
2
Aún más fácil: pyplot.hist([x, y], bins, alpha=0.5, label=['x', 'y']).
Augustin
174

Las respuestas aceptadas proporcionan el código para un histograma con barras superpuestas, pero en caso de que desee que cada barra esté una al lado de la otra (como lo hice yo), intente la siguiente variación:

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()

ingrese la descripción de la imagen aquí

Referencia: http://matplotlib.org/examples/statistics/histogram_demo_multihist.html

EDITAR [2018/03/16]: actualizado para permitir el trazado de matrices de diferentes tamaños, como lo sugiere @stochastic_zeitgeist

Gustavo Bezerra
fuente
@GustavoBezerra, ¿cómo usarlo plt.histpara producir un archivo pdf para cada histograma? Cargué mis datos usando pandas.read_csvy el archivo tiene 36 columnas y 100 líneas. Entonces me gustaría 100 archivos pdf.
Sigur
2
@Sigur Eso está bastante fuera de tema. Google o haga una nueva pregunta. Esto parece estar relacionado: stackoverflow.com/questions/11328958/…
Gustavo Bezerra
1
@stochastic_zeitgeist Estoy de acuerdo con @pasbi. Usé tu comentario con un marco de datos de pandas porque necesitaba diferentes pesos debido a nans. con x=np.array(df.a)y y=np.array(df.b.dropna())básicamente terminó siendoplt.hist([x, y], weights=[np.ones_like(x)/len(x), np.ones_like(y)/len(y)])
grinsbaeckchen
1
En caso de que sus tamaños de muestra sean drásticamente diferentes, es posible que desee trazar utilizando ejes gemelos para comparar mejor las distribuciones. Ver abajo .
Andrew
1
@ AgapeGal'lo Consulte la respuesta de Andrew.
Gustavo Bezerra
30

En el caso de que tenga diferentes tamaños de muestra, puede ser difícil comparar las distribuciones con un solo eje y. Por ejemplo:

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

En este caso, puede trazar sus dos conjuntos de datos en diferentes ejes. Para hacerlo, puede obtener sus datos de histograma usando matplotlib, borrar el eje y luego volver a trazarlo en dos ejes separados (desplazando los bordes del contenedor para que no se superpongan):

#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

Andrés
fuente
1
Esta es una buena respuesta breve, excepto que también debe agregar cómo centrar las barras en cada etiqueta de marca
Odisseo
12

Como un complemento a la respuesta de Gustavo Bezerra :

Si desea que cada histograma se normalice ( normedpara mpl <= 2.1 y densitypara mpl> = 3.1 ) no puede simplemente usarlo normed/density=True, debe establecer los pesos para cada valor:

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()

ingrese la descripción de la imagen aquí

Como comparación, exactamente lo mismo xy los yvectores con pesos predeterminados y density=True:

ingrese la descripción de la imagen aquí

jojo
fuente
9

Debe utilizar binslos valores devueltos por 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)

Dos histogramas matplotlib con el mismo binning

Adrien Renaud
fuente
7

Aquí hay un método simple para trazar dos histogramas, con sus barras una al lado de la otra, en la misma trama cuando los datos tienen diferentes tamaños:

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()
stochastic_zeitgeist
fuente
2

En caso de que tenga pandas ( import pandas as pd) o esté de acuerdo con su uso:

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()
serv-inc
fuente
Creo que el uso de pandas no funcionará si los histogramas a comparar tienen diferentes tamaños de muestra. Este también es a menudo el contexto en el que se utilizan histogramas normalizados.
Solomon Vimal
2

Hay una advertencia cuando desea trazar el histograma a partir de una matriz numpy en 2-d. Necesitas intercambiar los 2 ejes.

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()

ingrese la descripción de la imagen aquí

黄锐铭
fuente
0

Esta pregunta ha sido respondida anteriormente, pero quería agregar otra solución rápida / fácil que podría ayudar a otros visitantes a esta pregunta.

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

Aquí hay algunos ejemplos útiles para la comparación de kde vs histograma.

Solomon Vimal
fuente
0

Inspirado por la respuesta de Salomón, pero para seguir con la pregunta, que está relacionada con el histograma, una solución limpia es:

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

Asegúrese de trazar primero el más alto; de lo contrario, deberá establecer plt.ylim (0,0.45) para que el histograma más alto no se corte.

Sarah
fuente
0

También una opción que es bastante similar a la respuesta 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()

Da el siguiente resultado:

ingrese la descripción de la imagen aquí

PV8
fuente