W3docs

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 numpy

Si 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) o bins='sturges' — Matplotlib delega en np.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 scipy

Comparar 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ámetroTipoDescripción
binsint, list o strNúmero de bins, bordes explícitos o nombre de regla ('auto', 'fd', 'sturges')
densityboolNormaliza de modo que el área total = 1 (densidad de probabilidad)
cumulativeboolCada barra muestra el conteo/densidad acumulado hasta ese bin
orientationstr'vertical' (predeterminado) u 'horizontal'
colorstrColor de relleno de las barras
edgecolorstrColor del borde de las barras; 'white' da un separador limpio
alphafloat 0–1Transparencia; establécelo por debajo de 1 al superponer histogramas
labelstrEtiqueta de leyenda para este histograma
histtypestr'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. Usa np.random.default_rng(seed) para ejemplos reproducibles.
  • density vs normed. El antiguo parámetro normed=True fue eliminado en Matplotlib 3.x. Usa siempre density=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=True para 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 a show() y luego a savefig(), obtendrás un archivo en blanco. Llama siempre a savefig() antes de show().

Capítulos Relacionados

Was this page helpful?