HOWTO · Matplotlib
Establecer el tamaño de los marcadores en un gráfico de dispersión de Matplotlib
Establezca un tamaño uniforme o un tamaño por punto con scatter de Matplotlib, escale correctamente las áreas y corrija arrays de tamaño incompatible.
En esta página
Usa el argumento s de Axes.scatter para establecer el tamaño de los marcadores en un gráfico de dispersión de Matplotlib. Un escalar aplica la misma área nominal a todos los puntos; una lista o matriz unidimensional asigna un área a cada punto y debe tener la misma longitud que x y y.
Establecer el tamaño de los marcadores con s
Pase el argumento s a Axes.scatter para establecer el tamaño de los marcadores en un gráfico de dispersión de Matplotlib. Un escalar como s=80 asigna la misma área nominal a todos los puntos. Una lista o un array unidimensional asigna un área diferente a cada punto, pero debe contener la misma cantidad de valores que x e y. Por ejemplo, use ax.scatter(x, y, s=80) para un solo tamaño o ax.scatter(x, y, s=[20, 40, 80, 160]) para cuatro puntos con tamaños distintos.
Use scatter cuando el tamaño varíe entre observaciones. El método relacionado plot utiliza markersize para una única dimensión lineal del marcador.
Establecer un tamaño único o uno por punto
El siguiente ejemplo determinista coloca una colección de tamaño escalar junto a otra cuyos tamaños se calculan a partir de los datos. También asigna los mismos valores al color y emplea alpha para que los marcadores superpuestos se distingan mejor. La imagen guardada permite comprobar el resultado esperado sin un backend interactivo.
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [2, 3, 5, 7, 11]
values = [1, 2, 4, 7, 11]
sizes = [40 + value * 20 for value in values]
fig, (top, bottom) = plt.subplots(2, 1, figsize=(6, 7), layout="constrained")
uniform = top.scatter(x, y, s=80, color="tab:blue")
varying = bottom.scatter(x, y, s=sizes, c=values, cmap="viridis", alpha=0.75)
top.set(title="s = 80", xlabel="x", ylabel="y")
bottom.set(title="s = [60, 80, 120, 180, 260]", xlabel="x", ylabel="y")
fig.colorbar(varying, ax=bottom)
print("scalar sizes:", uniform.get_sizes().tolist())
print("per-point sizes:", varying.get_sizes().tolist())
fig.savefig("scatter-marker-sizes.webp", dpi=120)
Los dos objetos PathCollection informan de los valores exactos de s que conservó Matplotlib:
scalar sizes: [80]
per-point sizes: [60, 80, 120, 180, 260]
La transformación comienza en 40 y suma 20 por cada unidad de datos. Así, incluso el valor más pequeño permanece visible y el mayor no domina los ejes. Para datos reales, elija y documente un límite inferior y otro superior en vez de pasar directamente valores brutos extremos. El color puede codificar la misma variable, como en el ejemplo, u otra distinta; incluya una barra de color siempre que los colores representen una magnitud cuantitativa.
Entender los puntos cuadrados y el valor predeterminado
La referencia de scatter de Matplotlib define s en puntos cuadrados (pt²), donde un punto tipográfico equivale a 1/72 de pulgada. Su valor predeterminado es rcParams['lines.markersize'] ** 2. Por tanto, s es proporcional al área nominal del marcador, pero s=100 no garantiza una forma visible de 10 por 10 puntos. La trayectoria del marcador, los DPI de la figura y el estilo del borde afectan a su contorno final.
Los bordes influyen sobre todo en los marcadores pequeños. Cuando linewidths es mayor que cero y edgecolors es visible, el trazo se centra en el límite del marcador y aumenta su extensión efectiva en la mitad del ancho de línea. Use linewidths=0 o edgecolors="none" al comparar marcadores pequeños rellenos si el borde no debe alterar su tamaño aparente.
El tamaño del marcador es una medida del espacio de visualización, no una unidad de datos de los ejes x o y. Por ello, ampliar el gráfico o cambiar los límites de los ejes no convierte el marcador en un radio expresado en coordenadas de datos.
Escalar correctamente el área y el ancho lineal
Multiplicar s por dos duplica el área nominal. Como la extensión lineal crece aproximadamente con la raíz cuadrada del área, multiplicar s por cuatro hace que un marcador de forma similar sea aproximadamente el doble de ancho. Es una regla visual proporcional, no una garantía geométrica exacta para todas las trayectorias de marcador.
import matplotlib.pyplot as plt
base = 36
area_sizes = [base, 2 * base, 4 * base]
fig, (top, bottom) = plt.subplots(2, 1, figsize=(6, 7), layout="constrained")
top.scatter([1, 2, 3], [1, 1, 1], s=area_sizes, linewidths=0)
top.set(title="s = [36, 72, 144]", yticks=[])
top.set_xticks([1, 2, 3], ["1", "2", "4"])
bottom.scatter([1], [1], s=10**2, linewidths=0)
(line,) = bottom.plot([2], [1], marker="o", linestyle="none", markersize=10,
color="tab:orange")
bottom.set(title="scatter: s = 10**2 plot: markersize = 10", xlim=(0.5, 2.5), yticks=[])
print("scatter areas:", area_sizes)
print("plot markersize:", line.get_markersize())
fig.savefig("scatter-size-scaling.webp", dpi=120)
Los valores elegidos y la dimensión del marcador Line2D son:
scatter areas: [36, 72, 144]
plot markersize: 10.0
Elegir entre scatter(s=...) y plot(markersize=...)
Use scatter(s=...) para un gráfico de dispersión, especialmente cuando cada observación necesite su propia área o color. Use plot(marker="o", markersize=10) al dibujar un objeto Line2D cuyos nodos compartan el mismo tamaño de marcador. Como indica la referencia de plot de Matplotlib, markersize (o ms) es un tamaño lineal en puntos, no el área en puntos cuadrados que acepta scatter.
Aunque scatter(s=10**2) y plot(markersize=10) expresan dimensiones nominales relacionadas para marcadores circulares, no dependa de una igualdad píxel por píxel. Los marcadores de colecciones y líneas pueden diferir por sus trayectorias normalizadas, bordes, backend de renderizado y rasterización.
Corregir un array de tamaños que no coincide con los puntos
Un valor no escalar de s debe tener la misma longitud que los puntos dibujados. Este ejemplo captura el diagnóstico actual de Matplotlib para poder mostrarlo sin un traceback completo:
import matplotlib.pyplot as plt
x = [1, 2, 3]
y = [1, 4, 9]
try:
plt.scatter(x, y, s=[40, 80])
except ValueError as error:
print(f"{type(error).__name__}: {error}")
else:
raise AssertionError("a mismatched size array should fail")
Con Matplotlib 3.11.2, la salida es:
ValueError: s (size 2) cannot be broadcast to match x and y (size 3)
Las versiones anteriores de Matplotlib pueden formular el ValueError de otra manera, pero la corrección es la misma: proporcione tres valores de tamaño para tres puntos (x, y) o pase un único tamaño escalar.