Histogramas con Matplotlib en Python — Guía Completa
Aprende a crear y personalizar histogramas en Python con Matplotlib. Cubre bins, densidad, superposición KDE, acumulativos, apilados y guardado en archivo.
La función hist() de Matplotlib facilita visualizar cómo se distribuye un conjunto de datos a lo largo de un rango numérico. Este capítulo cubre todo, desde un primer histograma mínimo hasta técnicas prácticas que usarás en proyectos reales: elegir el número correcto de bins, graficar densidad en lugar de conteos sin procesar, superponer una curva KDE, comparar múltiples distribuciones y guardar el resultado en un archivo.
Antes de comenzar, asegúrate de tener Matplotlib y NumPy instalados:
pip install matplotlib numpySi eres nuevo en la biblioteca, consulta primero los capítulos de Introducción a Matplotlib y Primeros pasos.
¿Qué es un Histograma?
Un histograma divide una variable numérica continua en intervalos de igual tamaño llamados bins y dibuja una barra para cada bin cuya altura equivale al número de observaciones que caen en ese intervalo. A diferencia de un gráfico de barras, que compara categorías discretas, un histograma revela la forma de una distribución — si es simétrica, sesgada, bimodal o tiene valores atípicos.
Usa un histograma cuando quieras responder preguntas como:
- ¿Dónde se concentra la mayor parte de los datos?
- ¿La distribución es aproximadamente normal o está sesgada?
- ¿Hay brechas o múltiples picos (datos bimodales)?
- ¿Hay valores atípicos muy alejados del grueso de los datos?
Crear un Histograma Básico
Pasa un array unidimensional o una lista a plt.hist() y Matplotlib elige automáticamente el número de bins:
import matplotlib.pyplot as plt
import numpy as np
# Reproducible example: 1 000 values from a normal distribution
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
plt.hist(data)
plt.title('Basic Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()plt.tight_layout() evita que las etiquetas de los ejes queden cortadas — un buen hábito que conviene añadir antes de cada llamada a show() o savefig().
Elegir el Número de Bins
El parámetro bins es el ajuste más importante de un histograma. Pocos bins ocultan la estructura; demasiados crean ruido.
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
for ax, n_bins in zip(axes, [5, 30, 100]):
ax.hist(data, bins=n_bins, color='steelblue', edgecolor='white')
ax.set_title(f'bins={n_bins}')
ax.set_xlabel('Value')
ax.set_ylabel('Frequency')
plt.suptitle('Effect of Bin Count', y=1.02)
plt.tight_layout()
plt.show()Pautas prácticas:
- 5–10 bins — útil para conjuntos de datos muy pequeños (n < 50) o resúmenes rápidos.
- 20–50 bins — buen valor predeterminado para la mayoría de los conjuntos de datos (n = 100–10 000).
- 50–100+ bins — adecuado para conjuntos de datos grandes (n > 10 000) donde importan los detalles finos.
- Pasa una regla en string en lugar de un número:
bins='auto',bins='fd'(Freedman–Diaconis) obins='sturges'— Matplotlib delega ennp.histogram_bin_edges()de NumPy.
También puedes pasar una lista explícita de bordes de bins para un espaciado no uniforme:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.exponential(scale=2, size=1000)
# Finer bins near 0, coarser bins in the tail
edges = [0, 0.5, 1, 1.5, 2, 3, 4, 6, 8, 12]
plt.hist(data, bins=edges, color='darkorange', edgecolor='white')
plt.title('Custom Bin Edges (Exponential Data)')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()Personalizar la Apariencia
Color, Transparencia y Borde
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=5, scale=1.5, size=800)
plt.hist(
data,
bins=30,
color='steelblue',
edgecolor='white', # thin white line between bars
linewidth=0.5,
alpha=0.85, # slight transparency
)
plt.title('Styled Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()alpha (0 = totalmente transparente, 1 = totalmente opaco) es especialmente útil al superponer múltiples histogramas para que las barras no se oculten entre sí.
Eliminar el Ruido Visual
Eliminar los bordes superior y derecho le da al histograma un aspecto más limpio:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
fig, ax = plt.subplots(figsize=(8, 4))
ax.hist(data, bins=30, color='cornflowerblue', edgecolor='white')
ax.set_title('Clean Histogram')
ax.set_xlabel('Value')
ax.set_ylabel('Frequency')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.tight_layout()
plt.show()Histogramas de Densidad
Por defecto, hist() muestra conteos sin procesar en el eje y. Pasa density=True para normalizar el eje y de modo que el área total de todas las barras sea igual a 1. Esto convierte el histograma en una estimación de densidad de probabilidad, lo que facilita comparar conjuntos de datos de diferentes tamaños.
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
plt.hist(data, bins=30, density=True, color='mediumseagreen', edgecolor='white')
plt.title('Density Histogram')
plt.xlabel('Value')
plt.ylabel('Probability Density')
plt.tight_layout()
plt.show()Nota: los valores del eje y son densidades, no probabilidades. Multiplica una densidad por el ancho del bin para obtener la probabilidad de ese bin.
Superponer una Curva KDE
Una estimación de densidad kernel (KDE) es una curva suave que aproxima la distribución de probabilidad subyacente. Superponerla en un histograma de densidad ofrece una imagen intuitiva de la forma de la distribución. Usa scipy.stats.gaussian_kde para calcularla:
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
fig, ax = plt.subplots(figsize=(8, 4))
# Density histogram
ax.hist(data, bins=30, density=True,
color='steelblue', edgecolor='white', alpha=0.6, label='Histogram')
# KDE curve
xs = np.linspace(data.min(), data.max(), 300)
kde = gaussian_kde(data)
ax.plot(xs, kde(xs), color='navy', linewidth=2, label='KDE')
ax.set_title('Histogram with KDE Overlay')
ax.set_xlabel('Value')
ax.set_ylabel('Probability Density')
ax.legend()
plt.tight_layout()
plt.show()Instala SciPy si aún no está disponible:
pip install scipyComparar Múltiples Distribuciones
Para comparar dos o más distribuciones en los mismos ejes, llama a hist() varias veces y usa alpha para mantener las barras semitransparentes. Establece los mismos bins para ambas para que los anchos de las barras sean comparables:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
group_a = rng.normal(loc=0, scale=1, size=500)
group_b = rng.normal(loc=2, scale=1.5, size=500)
shared_bins = np.linspace(-5, 8, 40)
plt.hist(group_a, bins=shared_bins, alpha=0.6, color='steelblue', label='Group A')
plt.hist(group_b, bins=shared_bins, alpha=0.6, color='darkorange', label='Group B')
plt.title('Overlapping Histograms')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.legend()
plt.tight_layout()
plt.show()Definir shared_bins mediante np.linspace() garantiza que ambos histogramas usen bordes de bins idénticos y que sus barras se alineen visualmente.
Histogramas en Paralelo (Apilados)
Cuando la superposición dificulta la lectura de las distribuciones individuales, usa plt.subplots() para colocarlos uno al lado del otro:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
group_a = rng.normal(loc=0, scale=1, size=500)
group_b = rng.normal(loc=2, scale=1.5, size=500)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), sharey=True)
ax1.hist(group_a, bins=30, color='steelblue', edgecolor='white')
ax1.set_title('Group A')
ax1.set_xlabel('Value')
ax1.set_ylabel('Frequency')
ax2.hist(group_b, bins=30, color='darkorange', edgecolor='white')
ax2.set_title('Group B')
ax2.set_xlabel('Value')
plt.suptitle('Side-by-Side Histograms')
plt.tight_layout()
plt.show()sharey=True sincroniza ambos subplots con la misma escala en el eje y, de modo que las alturas de las barras son directamente comparables. Consulta el capítulo Subplots de Matplotlib para más opciones de diseño.
Histogramas Acumulativos
Pasa cumulative=True para dibujar un histograma donde cada barra representa el conteo total de observaciones hasta ese bin inclusive. Esto es útil para responder preguntas como "¿qué fracción de valores está por debajo de X?":
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
plt.hist(data, bins=30, cumulative=True, density=True,
color='mediumpurple', edgecolor='white')
plt.title('Cumulative Density Histogram')
plt.xlabel('Value')
plt.ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()Combinado con density=True, el histograma acumulativo se convierte en una aproximación escalonada de la CDF empírica (función de distribución acumulada). El eje y va de 0 a 1.
Orientación del Histograma
Pasa orientation='horizontal' para dibujar barras que se extienden hacia la izquierda desde el eje y. Rara vez es la opción predeterminada, pero refleja el diseño de los gráficos de distribución marginal de un diagrama de dispersión:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=500)
plt.hist(data, bins=20, orientation='horizontal',
color='tomato', edgecolor='white')
plt.title('Horizontal Histogram')
plt.ylabel('Value')
plt.xlabel('Frequency')
plt.tight_layout()
plt.show()Guardar un Histograma en un Archivo
Usa plt.savefig() antes de plt.show() (o en su lugar). Especifica el formato mediante la extensión del archivo:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
plt.hist(data, bins=30, color='steelblue', edgecolor='white')
plt.title('Distribution of Sample Data')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.savefig('histogram.png', dpi=150) # raster, good for web
plt.savefig('histogram.pdf') # vector, good for print/publication
plt.show()Formatos comunes: png, pdf, svg, eps. Usa svg o pdf cuando necesites una imagen escalable y de calidad para impresión.
Parámetros Clave de hist() — Referencia Rápida
| Parámetro | Tipo | Descripción |
|---|---|---|
bins | int, list o str | Número de bins, bordes explícitos o nombre de regla ('auto', 'fd', 'sturges') |
density | bool | Normaliza de modo que el área total = 1 (densidad de probabilidad) |
cumulative | bool | Cada barra muestra el conteo/densidad acumulado hasta ese bin |
orientation | str | 'vertical' (predeterminado) u 'horizontal' |
color | str | Color de relleno de las barras |
edgecolor | str | Color del borde de las barras; 'white' da un separador limpio |
alpha | float 0–1 | Transparencia; establécelo por debajo de 1 al superponer histogramas |
label | str | Etiqueta de leyenda para este histograma |
histtype | str | 'bar' (predeterminado), 'step', 'stepfilled' |
range | (min, max) | Recorta los datos a este rango antes de agrupar en bins |
Problemas Comunes
- Semillas aleatorias.
np.random.randn()produce valores diferentes en cada ejecución. Usanp.random.default_rng(seed)para ejemplos reproducibles. densityvsnormed. El antiguo parámetronormed=Truefue eliminado en Matplotlib 3.x. Usa siempredensity=True.- Comparar histogramas con diferentes tamaños de muestra. Las barras de frecuencia sin procesar no son comparables cuando los tamaños de los grupos difieren — usa
density=Truepara normalizar ambos. - Datos enteros discretos. Para enteros (p. ej., tiradas de dados, calificaciones de encuestas), establece los bordes de bins en medios enteros —
bins=[0.5, 1.5, 2.5, ..., 6.5]— de modo que cada entero caiga exactamente en un bin sin ambigüedad en los bordes. plt.show()borra la figura. Si llamas ashow()y luego asavefig(), obtendrás un archivo en blanco. Llama siempre asavefig()antes deshow().
Capítulos Relacionados
- Introducción a Matplotlib — descripción general de la biblioteca e instalación
- Primeros pasos con Matplotlib — guía del primer gráfico
- Gráficos de Barras con Matplotlib — comparar categorías discretas
- Diagramas de Dispersión con Matplotlib — relaciones entre dos variables
- Gráficos de Tarta con Matplotlib — proporciones de partes respecto al todo
- Etiquetas en Matplotlib — etiquetas de ejes, títulos y anotaciones
- Subplots en Matplotlib — múltiples gráficos en una figura
- Distribución de Datos — fundamentos estadísticos sobre distribuciones