HOWTO · Matplotlib

Cómo trazar un histograma para una lista de datos en Matplotlib

Traza un histograma a partir de una lista de Python con plt.hist() de Matplotlib y luego ajusta sus bins, densidad y color.

En esta página

Para trazar un histograma a partir de una lista de Python, pasa la lista directamente a la función plt.hist() de Matplotlib. La función agrupa los valores en intervalos llamados bins, cuenta cuántos valores caen en cada intervalo y dibuja una barra por intervalo, de modo que la forma del gráfico muestra cómo se distribuyen los valores. Una lista de Python normal sirve como entrada; no necesitas convertirla antes a un arreglo de NumPy ni a una Series de pandas. Este artículo traza una lista de 21 números enteros del 0 al 9, luego ajusta los bins, las alturas de las barras y el color de las barras, y por último muestra qué ocurre con un número de bins no válido y con una lista vacía.

Trazar un histograma a partir de una lista con plt.hist()

Sin argumentos adicionales, plt.hist() divide el rango de datos en diez bins de igual ancho. El ejemplo también captura los tres valores que devuelve la función y los imprime, para que veas exactamente cómo se agrupó la lista:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
counts, bin_edges, patches = plt.hist(data)
print("counts:", counts.tolist())
print("bin_edges:", bin_edges.tolist())
plt.xlabel("Value")
plt.ylabel("Count")
plt.title("Histogram of data")
plt.show()
counts: [1.0, 1.0, 2.0, 2.0, 5.0, 3.0, 3.0, 2.0, 1.0, 1.0]
bin_edges: [0.0, 0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9.0]

Histograma de los 21 valores de la lista dibujado por plt.hist() con los diez bins predeterminados; la barra más alta cubre los valores de 3,6 a 4,5 y tiene un conteo de 5.

plt.hist() devuelve tres valores. counts contiene el número de observaciones en cada bin, bin_edges da los límites de los intervalos y patches contiene los artistas de las barras por si necesitas cambiar el estilo de barras individuales después. El número de entradas de counts es el número de bins, mientras que bin_edges siempre tiene una entrada más porque marca ambos extremos de cada intervalo. Aquí el rango de datos va de 0 a 9, así que cada uno de los diez bins predeterminados mide 0,9 de ancho, y la barra más alta es el intervalo de 3,6 a 4,5, que contiene las cinco apariciones del valor 4. Puedes omitir la asignación y llamar solo a plt.hist(data) si únicamente necesitas el gráfico.

Establecer el número de bins o los bordes de los bins

El parámetro bins controla cómo se dividen los datos. Pasar un entero como bins=20 crea esa cantidad de intervalos de igual ancho a lo largo del rango de datos:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
counts, bin_edges, patches = plt.hist(data, bins=20)
print("counts:", counts.tolist())
print("number of bins:", len(counts))
plt.xlabel("Value")
plt.ylabel("Count")
plt.title("Histogram with 20 bins")
plt.show()
counts: [1.0, 0.0, 1.0, 0.0, 2.0, 0.0, 2.0, 0.0, 5.0, 0.0, 0.0, 3.0, 0.0, 3.0, 0.0, 2.0, 0.0, 1.0, 0.0, 1.0]
number of bins: 20

La misma lista trazada con 20 bins; muchos de los bins estrechos quedan vacíos porque los datos solo contienen números enteros del 0 al 9.

Veinte bins son demasiados para esta lista pequeña: como los valores son enteros, uno de cada dos bins queda vacío y la distribución parece más ruidosa de lo que es. En general, muy pocos bins pueden ocultar la estructura de los datos, mientras que muchos bins estrechos pueden hacer que un conjunto de datos pequeño parezca irregular, así que elige un número de bins acorde con el tamaño y la dispersión de tu lista. En lugar de un entero, puedes pasar una secuencia de valores de borde como bins=[0, 2, 4, 6, 8, 10] para fijar tú mismo los límites de los intervalos; los intervalos pueden tener entonces anchos diferentes. Cuando los conteos abarcan un rango amplio, el argumento opcional log=True cambia el eje de conteos a una escala logarítmica para que las barras más pequeñas sigan siendo visibles junto a una alta. Solo cambia la escala del eje, no los valores de entrada ni la división en bins.

Mostrar la densidad de probabilidad en lugar de los conteos

Por defecto, las alturas de las barras son conteos. Establece density=True cuando quieras una densidad de probabilidad en su lugar. Matplotlib escala entonces las alturas para que el área total de las barras sea 1:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
counts, bin_edges, patches = plt.hist(data, bins=10, density=True)
print("densities:", [round(d, 4) for d in counts.tolist()])
area = sum(d * (bin_edges[i + 1] - bin_edges[i]) for i, d in enumerate(counts))
print("total area:", round(area, 6))
plt.xlabel("Value")
plt.ylabel("Probability density")
plt.title("Probability histogram of data")
plt.show()
densities: [0.0529, 0.0529, 0.1058, 0.1058, 0.2646, 0.1587, 0.1587, 0.1058, 0.0529, 0.0529]
total area: 1.0

Histograma de densidad de probabilidad de la lista dibujado con density=True; las alturas de las barras son densidades y el área total de las barras es 1.

Con density=True, el primer valor de la tupla devuelta contiene densidades en lugar de conteos, y el ejemplo verifica la normalización multiplicando cada densidad por el ancho de su bin y sumando las áreas, lo que da exactamente 1. Ten en cuenta que la altura de una barra individual es una densidad, no la probabilidad de su intervalo; la probabilidad de un intervalo es el área de su barra, es decir, la altura multiplicada por el ancho. Esta distinción importa cuando los bins tienen anchos diferentes, porque una barra ancha puede representar más probabilidad que una barra estrecha más alta. Recuerda actualizar la etiqueta del eje vertical al cambiar, como hace el ejemplo, para que el gráfico explique qué significan las alturas de sus barras.

Cambiar el color de las barras

El parámetro color establece el color de relleno de las barras:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
plt.hist(data, bins=10, color="red")
plt.xlabel("Value")
plt.ylabel("Count")
plt.title("Red histogram of data")
plt.show()

El histograma de la lista con barras rojas, dibujado con diez bins y el parámetro color establecido en rojo.

Funciona cualquier especificación de color de Matplotlib, como un nombre como "red", una cadena hexadecimal como "#1f77b4" o un código de una sola letra. Las opciones de visualización se combinan libremente en una sola llamada; por ejemplo, plt.hist(data, bins=20, density=True, color="red") dibuja veinte barras rojas con alturas de densidad. Combines lo que combines, mantén el título y las etiquetas de los ejes en consonancia con esas opciones: un gráfico de densidad no debería conservar una etiqueta de conteos, y un gráfico con veinte bins no debería afirmar que muestra los diez predeterminados.

Gestionar un número de bins no válido y una lista vacía

Conviene conocer dos errores de entrada antes de que te sorprendan. El primero es un número de bins que no es positivo:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
plt.hist(data, bins=0)
Traceback (most recent call last):
  ...
ValueError: `bins` must be positive, when an integer

bins=0 (y cualquier entero negativo) produce un ValueError y no se dibuja nada. Si el número de bins procede de una entrada del usuario o de un cálculo, valida que sea un entero positivo antes de trazar, o pasa en su lugar una secuencia de bordes. El segundo caso es una lista vacía, que se comporta de forma muy distinta: no es un error en absoluto.

import matplotlib.pyplot as plt

counts, bin_edges, patches = plt.hist([])
print("counts:", counts.tolist())
print("bin_edges:", [round(e, 2) for e in bin_edges.tolist()])
counts: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
bin_edges: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

Sin datos, plt.hist() recurre al rango predeterminado de 0 a 1, devuelve diez conteos a cero y dibuja unos ejes vacíos sin lanzar ninguna excepción. Eso convierte la lista vacía en un modo de fallo silencioso: si tus datos proceden de un archivo, una consulta o un filtro, comprueba que la lista no esté vacía antes de trazar, para que un conjunto de datos ausente no se convierta en un gráfico en blanco que parece un resultado válido.