Histograma en Matplotlib: La guia completa de plt.hist() en Python
Updated on
Tienes un conjunto de datos con miles de valores numericos -- edades, puntuaciones de examenes, tiempos de respuesta, lecturas de sensores -- y necesitas entender como se distribuyen esos valores. Estan agrupados alrededor de un punto central? Sesgados hacia un extremo? Siguen una distribucion normal? Un diagrama de dispersion no ayudara. Un grafico de barras esta disenado para categorias, no para datos continuos. Lo que necesitas es un histograma, y en Python, matplotlib.pyplot.hist() es la forma estandar de construir uno.
El problema es que plt.hist() tiene mas de una docena de parametros, y la salida predeterminada a menudo se ve simple o enganosa. Elegir el numero incorrecto de bins puede ocultar patrones importantes en tus datos. Comparar multiples distribuciones en un solo grafico requiere conocer la combinacion correcta de opciones. Esta guia cubre cada parametro importante, con ejemplos de codigo funcionales que puedes copiar directamente en tu notebook o script.
Que es un histograma y cuando deberias usar uno?
Un histograma divide un rango de valores numericos en intervalos de igual ancho llamados bins y cuenta cuantos puntos de datos caen en cada bin. El eje x muestra el rango de valores, y el eje y muestra la frecuencia (conteo) o densidad para cada bin. A diferencia de un grafico de barras, que muestra datos categoricos, un histograma representa la distribucion de datos numericos continuos.
Usa un histograma cuando necesites:
- Ver la forma de una distribucion (normal, sesgada, bimodal, uniforme)
- Identificar valores atipicos o brechas en los datos
- Comparar la dispersion de valores entre grupos
- Decidir transformaciones de datos antes del modelado
Sintaxis basica de plt.hist()
El histograma mas simple requiere solo un argumento: el array de datos.
import matplotlib.pyplot as plt
import numpy as np
# Generate 1000 normally distributed values
np.random.seed(42)
data = np.random.normal(loc=50, scale=15, size=1000)
plt.hist(data)
plt.title('Basic Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()Por defecto, matplotlib divide los datos en 10 bins. La funcion devuelve tres objetos: los conteos de bins, los bordes de bins y los objetos patch (los rectangulos dibujados). Cubriremos esos valores de retorno en detalle mas adelante.
Firma completa
plt.hist(x, bins=None, range=None, density=False, weights=None,
cumulative=False, bottom=None, histtype='bar', align='mid',
orientation='vertical', rwidth=None, log=False, color=None,
label=None, stacked=False, edgecolor=None, alpha=None)Control de bins
El parametro bins es la configuracion mas importante en un histograma. Muy pocos bins ocultan patrones. Demasiados bins crean ruido.
Establecer un numero fijo de bins
fig, axes = plt.subplots(1, 3, figsize=(14, 4))
axes[0].hist(data, bins=5, edgecolor='black')
axes[0].set_title('5 Bins')
axes[1].hist(data, bins=30, edgecolor='black')
axes[1].set_title('30 Bins')
axes[2].hist(data, bins=100, edgecolor='black')
axes[2].set_title('100 Bins')
plt.tight_layout()
plt.show()Con 5 bins, solo ves una forma aproximada. Con 100 bins, los tamanos de muestra pequenos por bin introducen ruido visual. Para este conjunto de datos de 1,000 puntos, 30 bins produce una imagen clara de la distribucion normal.
Bordes de bins personalizados
Pasa una secuencia a bins para definir limites exactos:
custom_edges = [0, 20, 35, 50, 65, 80, 100]
plt.hist(data, bins=custom_edges, edgecolor='black', color='steelblue')
plt.title('Histogram with Custom Bin Edges')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()Esto es util cuando tus datos tienen umbrales significativos -- calificaciones con letras, rangos de edad o niveles de rendimiento.
Algoritmos automaticos de bins
Matplotlib soporta varios algoritmos que calculan el numero optimo de bins basado en las caracteristicas de los datos:
| Algoritmo | Valor de bins= | Metodo | Mejor para |
|---|---|---|---|
| Sturges | 'sturges' | 1 + log2(n) | Conjuntos de datos pequenos, aproximadamente normales |
| Scott | 'scott' | Basado en desviacion estandar y n | Datos normales o casi normales |
| Freedman-Diaconis | 'fd' | Basado en IQR y n | Robusto ante valores atipicos |
| Raiz cuadrada | 'sqrt' | sqrt(n) | Estimacion rapida aproximada |
| Auto | 'auto' | Maximo de Sturges y FD | Predeterminado de uso general |
fig, axes = plt.subplots(1, 3, figsize=(14, 4))
for ax, method in zip(axes, ['sturges', 'scott', 'fd']):
ax.hist(data, bins=method, edgecolor='black', color='#4C72B0')
ax.set_title(f'bins="{method}"')
plt.tight_layout()
plt.show()Para la mayoria de los casos, bins='auto' es un buen punto de partida. Cambia a 'fd' cuando tus datos contengan valores atipicos, ya que usa el rango intercuartilico en lugar de la desviacion estandar.
Histogramas normalizados y de densidad
Por defecto, el eje y muestra conteos sin procesar. Establece density=True para normalizar el histograma de modo que el area total bajo las barras sea igual a 1. Esto convierte el eje y de frecuencia a densidad de probabilidad.
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].hist(data, bins=30, edgecolor='black', color='#55A868')
axes[0].set_title('Frequency (default)')
axes[0].set_ylabel('Count')
axes[1].hist(data, bins=30, edgecolor='black', color='#C44E52', density=True)
axes[1].set_title('Density (density=True)')
axes[1].set_ylabel('Probability Density')
plt.tight_layout()
plt.show()La normalizacion por densidad es esencial cuando quieres superponer una curva de distribucion teorica o comparar conjuntos de datos de diferentes tamanos:
from scipy import stats
plt.hist(data, bins=30, density=True, edgecolor='black', color='#55A868', alpha=0.7)
# Overlay the theoretical normal curve
x_range = np.linspace(data.min(), data.max(), 200)
plt.plot(x_range, stats.norm.pdf(x_range, loc=50, scale=15), 'r-', linewidth=2, label='Normal PDF')
plt.legend()
plt.title('Density Histogram with Normal Curve Overlay')
plt.show()Personalizar la apariencia
Color, color de borde y transparencia
plt.hist(data, bins=30, color='#4C72B0', edgecolor='white', alpha=0.85)
plt.title('Styled Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()Tipos de histograma
El parametro histtype cambia el estilo visual:
Valor de histtype | Descripcion |
|---|---|
'bar' | Barras rellenas tradicionales (predeterminado) |
'barstacked' | Barras apiladas para multiples conjuntos de datos |
'step' | Contorno de linea sin relleno |
'stepfilled' | Area rellena con contorno escalonado |
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
types = ['bar', 'barstacked', 'step', 'stepfilled']
for ax, ht in zip(axes.flat, types):
ax.hist(data, bins=30, histtype=ht, edgecolor='black', color='#4C72B0')
ax.set_title(f'histtype="{ht}"')
plt.tight_layout()
plt.show()El tipo 'step' es particularmente util al superponer multiples distribuciones, ya que los contornos sin relleno no se ocultan entre si.
Multiples histogramas en un solo grafico
Histogramas superpuestos
Usa alpha (transparencia) para superponer dos o mas distribuciones:
np.random.seed(42)
group_a = np.random.normal(loc=50, scale=10, size=800)
group_b = np.random.normal(loc=65, scale=12, size=800)
plt.hist(group_a, bins=30, alpha=0.6, color='#4C72B0', edgecolor='black', label='Group A')
plt.hist(group_b, bins=30, alpha=0.6, color='#C44E52', edgecolor='black', label='Group B')
plt.legend()
plt.title('Overlapping Histograms')
plt.xlabel('Score')
plt.ylabel('Frequency')
plt.show()Histogramas lado a lado
Pasa una lista de arrays para graficarlos con barras agrupadas:
plt.hist([group_a, group_b], bins=20, color=['#4C72B0', '#C44E52'],
edgecolor='black', label=['Group A', 'Group B'])
plt.legend()
plt.title('Side-by-Side Histograms')
plt.xlabel('Score')
plt.ylabel('Frequency')
plt.show()Cuando pasas una lista de arrays, matplotlib coloca las barras de cada conjunto de datos una al lado de la otra dentro de cada bin.
Histogramas apilados
Establece stacked=True para apilar un conjunto de datos sobre otro. Esto muestra tanto las distribuciones individuales como su total combinado.
np.random.seed(42)
freshmen = np.random.normal(loc=68, scale=8, size=500)
sophomores = np.random.normal(loc=72, scale=7, size=400)
juniors = np.random.normal(loc=75, scale=6, size=300)
plt.hist([freshmen, sophomores, juniors], bins=25, stacked=True,
color=['#4C72B0', '#55A868', '#C44E52'], edgecolor='black',
label=['Freshmen', 'Sophomores', 'Juniors'])
plt.legend()
plt.title('Stacked Histogram: Exam Scores by Class Year')
plt.xlabel('Score')
plt.ylabel('Frequency')
plt.show()Los histogramas apilados funcionan bien cuando quieres mostrar como los subgrupos contribuyen a una distribucion general. Sin embargo, se vuelven dificiles de leer con mas de tres o cuatro grupos.
Histogramas acumulativos
Establece cumulative=True para mostrar como los valores se acumulan de izquierda a derecha. La ultima barra alcanza el conteo total (o 1.0 si density=True).
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].hist(data, bins=30, cumulative=True, edgecolor='black', color='#DD8452')
axes[0].set_title('Cumulative Histogram (Count)')
axes[0].set_ylabel('Cumulative Count')
axes[1].hist(data, bins=30, cumulative=True, density=True, edgecolor='black', color='#8172B3')
axes[1].set_title('Cumulative Histogram (Density)')
axes[1].set_ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()Los histogramas acumulativos son utiles para responder preguntas como "Que porcentaje de valores cae por debajo de 60?" leyendo directamente del eje y.
Histogramas horizontales
Establece orientation='horizontal' para invertir los ejes. Esto es util cuando las etiquetas de valores son largas o cuando quieres colocar el histograma junto a otro grafico vertical.
plt.hist(data, bins=30, orientation='horizontal', color='#64B5CD', edgecolor='black')
plt.title('Horizontal Histogram')
plt.xlabel('Frequency')
plt.ylabel('Value')
plt.show()Valores de retorno de plt.hist()
plt.hist() devuelve tres valores que te dan acceso programatico a los datos del histograma:
n, bin_edges, patches = plt.hist(data, bins=20, edgecolor='black', color='#4C72B0')
plt.show()
print(f"Bin counts (n): shape = {n.shape}, first 5 = {n[:5]}")
print(f"Bin edges: shape = {bin_edges.shape}, first 5 = {bin_edges[:5]}")
print(f"Patches: {len(patches)} Rectangle objects")| Valor de retorno | Tipo | Descripcion |
|---|---|---|
n | ndarray | Conteo (o densidad) para cada bin |
bin_edges | ndarray | Valores de borde para cada bin (longitud = len(n) + 1) |
patches | lista de Rectangles | Los objetos patch de matplotlib para cada barra |
Puedes usar patches para colorear barras individuales segun su altura o posicion:
n, bin_edges, patches = plt.hist(data, bins=30, edgecolor='black')
# Color bars based on height
for count, patch in zip(n, patches):
if count > 50:
patch.set_facecolor('#C44E52')
else:
patch.set_facecolor('#4C72B0')
plt.title('Conditional Bar Coloring')
plt.show()Referencia de parametros comunes de plt.hist()
| Parametro | Tipo | Descripcion | Predeterminado |
|---|---|---|---|
x | array-like | Datos de entrada | Requerido |
bins | int, secuencia o str | Numero de bins, bordes de bins o nombre de algoritmo | 10 |
range | tuple | Rango inferior y superior de los bins | (x.min(), x.max()) |
density | bool | Normalizar para que el area sea igual a 1 | False |
weights | array-like | Peso para cada punto de datos | None |
cumulative | bool | Calcular histograma acumulativo | False |
histtype | str | 'bar', 'barstacked', 'step', 'stepfilled' | 'bar' |
orientation | str | 'vertical' o 'horizontal' | 'vertical' |
color | color o lista | Color(es) de barra | None |
edgecolor | color | Color del borde de la barra | None |
alpha | float | Transparencia (0 a 1) | None |
label | str | Etiqueta para la leyenda | None |
stacked | bool | Apilar multiples conjuntos de datos | False |
log | bool | Eje y logaritmico | False |
rwidth | float | Ancho relativo de las barras (0 a 1) | None |
bottom | array-like o escalar | Linea base para cada barra | 0 |
plt.hist() vs sns.histplot(): Cuando usar cual
Si usas seaborn junto con matplotlib, quizas te preguntes que funcion de histograma usar. Aqui tienes una comparacion directa:
| Caracteristica | plt.hist() | sns.histplot() |
|---|---|---|
| Biblioteca | matplotlib | seaborn |
| Tipos de entrada | Array, lista, Series | Array, Series, columna de DataFrame |
| Superposicion KDE | Manual (se necesita scipy) | Integrado (kde=True) |
| Estilo predeterminado | Minimo | Listo para publicacion |
| Multiples grupos | Pasar lista de arrays | Parametro hue |
| Opciones de estadisticas | Conteo, densidad | Conteo, densidad, frecuencia, probabilidad, porcentaje |
| Algoritmos de bins | sturges, scott, fd, sqrt, auto | auto, fd, doane, scott, stone, rice, sturges, sqrt |
| Escala logaritmica | log=True | log_scale=True |
| Eje categorico | No soportado | Soportado via hue |
| Rendimiento (datos grandes) | Mas rapido | Ligeramente mas lento |
| Profundidad de personalizacion | API completa de matplotlib | API de seaborn + matplotlib |
Usa plt.hist() cuando necesites control total sobre cada elemento visual, cuando trabajes con subplots, o cuando seaborn no este disponible. Usa sns.histplot() cuando quieras superposiciones KDE, estilos predeterminados mas limpios, o necesites dividir datos por una variable categorica con codigo minimo.
Crea histogramas interactivos con PyGWalker
Los histogramas estaticos son excelentes para informes y scripts, pero durante el analisis exploratorio de datos a menudo necesitas cambiar bins, filtrar subconjuntos y cambiar entre tipos de graficos rapidamente. PyGWalker (opens in a new tab) es una biblioteca de Python de codigo abierto que convierte cualquier DataFrame de pandas o polars en una interfaz de visualizacion interactiva de arrastrar y soltar directamente dentro de Jupyter Notebook -- sin necesidad de codigo frontend.
pip install pygwalkerimport pandas as pd
import pygwalker as pyg
# Load your dataset into a DataFrame
df = pd.DataFrame({
'score': np.random.normal(70, 12, 2000),
'group': np.random.choice(['A', 'B', 'C'], 2000)
})
# Launch the interactive UI
walker = pyg.walk(df)Una vez que se abre la interfaz, arrastra score al eje x y PyGWalker genera automaticamente un histograma. Puedes ajustar el tamano del bin, dividir por group usando codificacion de color, cambiar al modo de densidad y exportar el grafico resultante -- todo sin escribir codigo adicional. Esto es especialmente util cuando necesitas explorar varias variables rapidamente antes de escribir el codigo final de matplotlib para un informe.
Preguntas frecuentes
Como elijo el numero correcto de bins para un histograma de matplotlib?
Comienza con bins='auto', que usa el maximo de los metodos Sturges y Freedman-Diaconis. Para datos con valores atipicos, usa bins='fd'. Para conjuntos de datos pequenos (menos de 200 puntos), bins='sturges' funciona bien. Tambien puedes pasar un entero y ajustar a ojo: aumenta el numero si la distribucion se ve demasiado suave, disminuyelo si las barras se ven ruidosas.
Cual es la diferencia entre density=True y cumulative=True en plt.hist()?
density=True normaliza el histograma para que el area total bajo todas las barras sea igual a 1, convirtiendo el eje y a densidad de probabilidad. cumulative=True hace que cada barra represente la suma de todas las barras anteriores mas si misma. Puedes combinar ambos: density=True, cumulative=True produce una funcion de distribucion acumulativa donde la ultima barra alcanza 1.0.
Como superpongo dos histogramas en matplotlib?
Llama a plt.hist() dos veces con el mismo valor de bins y establece alpha a un valor menor que 1 (por ejemplo, 0.5 o 0.6) para que ambas distribuciones permanezcan visibles. Agrega label a cada llamada y termina con plt.legend(). Usar histtype='step' como alternativa evita la necesidad de transparencia por completo ya que solo dibuja contornos.
Puede plt.hist() manejar pandas Series y columnas de DataFrame directamente?
Si. plt.hist() acepta cualquier entrada tipo array, incluyendo pandas Series. Puedes pasar df['column_name'] directamente. Para graficar desde un DataFrame usando el metodo integrado de pandas, usa df['column_name'].plot.hist(bins=30), que utiliza matplotlib internamente.
Como guardo un histograma de matplotlib como archivo de imagen?
Despues de llamar a plt.hist(), usa plt.savefig('histogram.png', dpi=150, bbox_inches='tight') antes de plt.show(). El parametro bbox_inches='tight' evita que las etiquetas se corten. Los formatos soportados incluyen PNG, PDF, SVG y EPS.