Gráficos de dispersión con Matplotlib en Python — Guía completa
Aprende a crear y personalizar gráficos de dispersión en Python con Matplotlib. Cubre color, tamaño, alpha, barras de color, grupos múltiples y anotaciones.
La función scatter() de Matplotlib te permite visualizar la relación entre dos variables numéricas colocando un marcador en cada punto de datos (x, y). A diferencia de un gráfico de líneas, los gráficos de dispersión no asumen ningún orden ni continuidad — cada punto existe por sí solo. Esto los convierte en la opción predilecta para explorar correlaciones, detectar agrupaciones e identificar valores atípicos.
Este capítulo cubre todo, desde tu primer gráfico de dispersión hasta técnicas profesionales: codificación de color y tamaño por punto, transparencia, barras de color, gráficos con múltiples grupos, anotaciones de puntos y guardado de archivos listos para publicación.
Antes de comenzar, asegúrate de que Matplotlib esté instalado:
pip install matplotlibSi eres nuevo en Matplotlib, lee primero los capítulos de Introducción a Matplotlib y Primeros pasos.
Cuándo usar un gráfico de dispersión
Usa un gráfico de dispersión cuando:
- Quieras explorar la correlación entre dos variables numéricas (altura vs. peso, horas de estudio vs. calificación).
- Necesites detectar agrupaciones — grupos de puntos que se concentran naturalmente.
- Quieras identificar valores atípicos — puntos alejados de la distribución principal.
- Estés codificando una tercera variable mediante el tamaño o el color del marcador (un "gráfico de burbujas" es un gráfico de dispersión donde el tamaño representa una tercera variable).
Evita los gráficos de dispersión cuando un eje represente categorías sin orden — un gráfico de barras es más claro en ese caso. Para tendencias a lo largo de una variable continua ordenada, un gráfico de líneas es más apropiado.
Creación de un gráfico de dispersión básico
Pasa dos secuencias de igual longitud — valores x y valores y — a plt.scatter():
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = [2, 4, 5, 4, 7, 8, 6, 9, 10, 12]
plt.scatter(x, y)
plt.title('Basic Scatter Plot')
plt.xlabel('X Values')
plt.ylabel('Y Values')
plt.tight_layout()
plt.show()plt.tight_layout() evita que las etiquetas queden recortadas — conviértelo en un hábito antes de cada llamada a show() o savefig().
Personalización del tamaño de los marcadores
El parámetro s controla el tamaño del marcador en puntos al cuadrado (el valor predeterminado es 20). Auméntalo para que los puntos sean más fáciles de ver, especialmente en presentaciones:
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5, 6, 7, 8]
y = [3, 1, 4, 1, 5, 9, 2, 6]
plt.scatter(x, y, s=120)
plt.title('Larger Markers')
plt.xlabel('X')
plt.ylabel('Y')
plt.tight_layout()
plt.show()También puedes pasar una lista o array a s para que cada punto tenga su propio tamaño — así es como se codifica una tercera variable numérica como tamaño de burbuja:
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [5, 3, 6, 2, 7]
sizes = [100, 300, 50, 400, 200] # third variable encoded as bubble area
plt.scatter(x, y, s=sizes)
plt.title('Bubble Chart — size encodes a third variable')
plt.xlabel('X')
plt.ylabel('Y')
plt.tight_layout()
plt.show()Personalización del color de los marcadores
Un solo color para todos los puntos
Pasa cualquier nombre de color, cadena hexadecimal o tupla RGB a c (o color):
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [2, 4, 1, 5, 3]
plt.scatter(x, y, s=100, c='steelblue')
plt.title('Single Color')
plt.xlabel('X')
plt.ylabel('Y')
plt.tight_layout()
plt.show()Color por punto a partir de una variable numérica
Pasar un array a c asigna a cada valor un color mediante el mapa de colores especificado en cmap. Añade plt.colorbar() para mostrar qué representan los colores:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=42)
x = rng.random(50)
y = rng.random(50)
values = rng.random(50) # third variable, e.g. intensity or temperature
scatter = plt.scatter(x, y, s=80, c=values, cmap='viridis')
plt.colorbar(scatter, label='Intensity')
plt.title('Color-Mapped Scatter Plot')
plt.xlabel('X')
plt.ylabel('Y')
plt.tight_layout()
plt.show()'viridis' es un mapa de colores perceptualmente uniforme que se puede leer en escala de grises y es accesible para personas con daltonismo. Otras buenas opciones son 'plasma', 'cividis' y 'coolwarm'.
Control de la transparencia con alpha
Cuando muchos puntos se superponen, forman una mancha opaca que oculta la densidad real. Establece alpha (0 = completamente transparente, 1 = completamente opaco) para revelar la estructura superpuesta:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=0)
x = rng.normal(loc=0, scale=1, size=300)
y = rng.normal(loc=0, scale=1, size=300)
plt.scatter(x, y, s=40, alpha=0.4)
plt.title('Transparent Markers Reveal Density (alpha=0.4)')
plt.xlabel('X')
plt.ylabel('Y')
plt.tight_layout()
plt.show()Un buen punto de partida es alpha=0.4 a 0.6. Ajústalo según la cantidad de puntos que tengas.
Estilo de los bordes de los marcadores
Usa edgecolors para añadir un borde alrededor de cada marcador y linewidths para controlar el grosor del borde. Esto ayuda a que los puntos resalten sobre un fondo con color:
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5, 6]
y = [3, 1, 4, 1, 5, 9]
plt.scatter(x, y, s=150, c='gold', edgecolors='black', linewidths=1.5)
plt.title('Markers with Edges')
plt.xlabel('X')
plt.ylabel('Y')
plt.tight_layout()
plt.show()Pasa edgecolors='none' para eliminar los bordes por completo (este es el valor predeterminado para la mayoría de los mapas de colores).
Representación de múltiples grupos
Para comparar grupos, llama a plt.scatter() una vez por grupo y asigna una etiqueta. Matplotlib asigna un color predeterminado diferente a cada llamada:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=7)
# Group A — centered around (2, 3)
ax_x = rng.normal(loc=2, scale=0.5, size=30)
ax_y = rng.normal(loc=3, scale=0.5, size=30)
# Group B — centered around (5, 6)
bx_x = rng.normal(loc=5, scale=0.5, size=30)
bx_y = rng.normal(loc=6, scale=0.5, size=30)
# Group C — centered around (8, 2)
cx_x = rng.normal(loc=8, scale=0.5, size=30)
cx_y = rng.normal(loc=2, scale=0.5, size=30)
plt.scatter(ax_x, ax_y, s=60, label='Group A')
plt.scatter(bx_x, bx_y, s=60, label='Group B')
plt.scatter(cx_x, cx_y, s=60, label='Group C')
plt.legend()
plt.title('Multi-Group Scatter Plot')
plt.xlabel('X')
plt.ylabel('Y')
plt.tight_layout()
plt.show()Cada llamada a scatter() elige automáticamente el siguiente color del ciclo de colores predeterminado. Pasa c='red' (o cualquier color) para anularlo.
Anotación de puntos individuales
Usa plt.annotate() para etiquetar puntos específicos — es útil para destacar valores atípicos u observaciones clave:
import matplotlib.pyplot as plt
cities = ['London', 'Berlin', 'Madrid', 'Rome', 'Paris']
population = [9.0, 3.7, 3.3, 2.8, 2.1] # millions
area = [1572, 892, 604, 1285, 105] # km²
plt.scatter(area, population, s=100, c='steelblue', edgecolors='black', linewidths=0.8)
for i, city in enumerate(cities):
plt.annotate(
city,
xy=(area[i], population[i]),
xytext=(8, 4), # offset in points
textcoords='offset points',
fontsize=9,
)
plt.title('European City Population vs. Area')
plt.xlabel('Area (km²)')
plt.ylabel('Population (millions)')
plt.tight_layout()
plt.show()El patrón xytext + textcoords='offset points' desplaza la etiqueta ligeramente para que no quede directamente sobre el marcador.
Uso de ejes logarítmicos
Cuando los datos abarcan varios órdenes de magnitud, los ejes lineales comprimen la mayoría de los puntos en una esquina. Cambia a escala logarítmica con plt.xscale('log') o plt.yscale('log'):
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=1)
x = np.logspace(1, 5, 60) # 10¹ to 10⁵
y = x * rng.uniform(0.5, 2.0, 60) # roughly proportional, with noise
plt.scatter(x, y, s=40, alpha=0.7)
plt.xscale('log')
plt.yscale('log')
plt.title('Log-Scale Scatter Plot')
plt.xlabel('X (log scale)')
plt.ylabel('Y (log scale)')
plt.tight_layout()
plt.show()Ambos ejes ahora abarcan intervalos iguales de potencias de diez, distribuyendo los datos de manera uniforme en el área del gráfico.
Añadir una línea de regresión
Un gráfico de dispersión muestra puntos individuales; añadir una línea de mejor ajuste revela la tendencia general. Calcula la pendiente y la ordenada al origen con np.polyfit():
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=3)
x = np.linspace(0, 10, 40)
y = 2.5 * x + rng.normal(scale=3, size=40) # linear trend + noise
# Fit a degree-1 polynomial (straight line)
slope, intercept = np.polyfit(x, y, 1)
trend_line = slope * x + intercept
plt.scatter(x, y, s=50, label='Data points', alpha=0.7)
plt.plot(x, trend_line, color='red', linewidth=2, label=f'Trend (slope={slope:.2f})')
plt.legend()
plt.title('Scatter Plot with Regression Line')
plt.xlabel('X')
plt.ylabel('Y')
plt.tight_layout()
plt.show()np.polyfit(x, y, 1) devuelve [slope, intercept] para la línea de mejor ajuste a través de los puntos.
Guardar un gráfico de dispersión en un archivo
Usa plt.savefig() en lugar de plt.show() para escribir el gráfico en el disco. Llámalo antes de plt.show() — después de show() la figura se borra:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=9)
x = rng.random(60)
y = rng.random(60)
plt.scatter(x, y, s=60, alpha=0.6, c='teal', edgecolors='white', linewidths=0.5)
plt.title('Saved Scatter Plot')
plt.xlabel('X')
plt.ylabel('Y')
plt.tight_layout()
plt.savefig('scatter.png', dpi=150) # PNG at 150 DPI
plt.savefig('scatter.pdf') # vector PDF — best for publication
plt.show()Opciones de formato comunes: 'png' (raster, web), 'pdf' (vector, publicación), 'svg' (vector, web). Aumenta dpi a 300 para rasters de calidad de impresión.
scatter() vs plot() — ¿Cuál usar?
Ambas funciones pueden dibujar gráficos solo con marcadores, pero sirven para propósitos distintos:
| Característica | plt.scatter() | plt.plot() |
|---|---|---|
Tamaño por punto (s) | Sí — pasa un array | No |
Color por punto (c) | Sí — pasa un array | No (un color por llamada) |
| Soporte de mapa de colores | Sí (cmap) | Limitado |
| Rendimiento en conjuntos de datos grandes | Más lento | Más rápido |
| Línea de conexión | No | Sí |
Usa scatter() cuando necesites estilo por punto (el color o el tamaño varía según los datos). Usa plot(marker='o', linestyle='None') para gráficos simples de marcadores en conjuntos de datos grandes donde la velocidad importa. Consulta el capítulo de Marcadores de Matplotlib para más información sobre estilos de marcadores.
Ejemplo completo
El siguiente script autónomo combina las técnicas más útiles — mapeado de colores, codificación de tamaño, transparencia, una barra de color y una leyenda:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=42)
n = 80
x = rng.standard_normal(n)
y = 0.8 * x + rng.standard_normal(n) * 0.6 # correlated
sizes = rng.uniform(30, 200, n) # bubble area
values = rng.random(n) # third variable for color
fig, ax = plt.subplots(figsize=(8, 5))
sc = ax.scatter(
x, y,
s=sizes,
c=values,
cmap='plasma',
alpha=0.75,
edgecolors='white',
linewidths=0.5,
)
plt.colorbar(sc, ax=ax, label='Intensity')
ax.set_title('Comprehensive Scatter Plot Example', fontsize=13)
ax.set_xlabel('X Variable')
ax.set_ylabel('Y Variable (correlated)')
fig.tight_layout()
plt.savefig('scatter_complete.png', dpi=150)
plt.show()Puntos clave:
fig, ax = plt.subplots()te proporciona objetos explícitos de figura y ejes — el enfoque recomendado para cualquier cosa más allá de un prototipo rápido.ax.scatter()en un objetoAxesse comporta de forma idéntica aplt.scatter().plt.colorbar(sc, ax=ax, label='...')adjunta la barra de color a los ejes específicos.
Buenas prácticas
- Muestra la escala. Si usas
ccon un mapa de colores, añade siempre una barra de color para que los lectores sepan qué representan los colores. - Evita el solapamiento excesivo de puntos. Para más de ~500 puntos, establece
alpha < 1o cambia a un histograma 2D (plt.hist2d()) o un gráfico hexbin (plt.hexbin()). - Elige mapas de colores accesibles.
'viridis','plasma'y'cividis'son perceptualmente uniformes y aptos para personas con daltonismo. Evita'jet'y'rainbow'. - Etiqueta los ejes con unidades.
plt.xlabel('Altura (cm)')es más informativo queplt.xlabel('Altura'). - Añade un título que indique el hallazgo. "Altura vs. Peso — correlación positiva" es más útil que "Gráfico de dispersión".
Capítulos relacionados
- Introducción a Matplotlib — descripción general de la biblioteca e instalación
- Primeros pasos con Matplotlib — tus primeras figuras
- Gráficos de líneas con Matplotlib — tendencias en variables continuas
- Gráficos de barras con Matplotlib — comparación de categorías discretas
- Histogramas con Matplotlib — distribución de una sola variable
- Marcadores de Matplotlib — todos los estilos de marcadores y opciones de personalización
- Subgráficos de Matplotlib — combinación de múltiples gráficos en una figura