HOWTO · Matplotlib
Gráfico de densidad con Matplotlib
Para generar la gráfica de densidad usando Python, primero estimamos la función de densidad a partir de los datos dados usando el método `gaussian_kde()` del módulo `scipy.stats` y luego trazamos la función de densidad para generar la gráfica de densidad.
En esta página
Un gráfico de densidad muestra una estimación suavizada de cómo se distribuyen los datos de una muestra unidimensional. En Python, la vía habitual es la estimación de densidad por núcleo (KDE): estimar una función de densidad a partir de las observaciones con gaussian_kde() del módulo scipy.stats, evaluarla en una cuadrícula de valores de x y dibujar la curva resultante con Matplotlib. Puedes llegar al mismo resultado con menos código mediante kdeplot() del paquete seaborn o estableciendo kind="density" en pandas.DataFrame.plot(). Los ejemplos siguientes usan NumPy, SciPy y Matplotlib; las variantes con Seaborn y pandas necesitan además que esos paquetes estén instalados, y el gráfico de pandas requiere SciPy porque llama internamente a gaussian_kde.
Crear un gráfico de densidad con scipy.stats.gaussian_kde
gaussian_kde construye la estimación colocando un núcleo —una pequeña protuberancia con forma de gaussiana— sobre cada observación y promediando esas protuberancias. El resultado es una función de densidad invocable: se le pasa una cuadrícula de posiciones de x y devuelve la densidad estimada en cada posición, que es exactamente la línea que dibuja Matplotlib.
Primero, ajusta la estimación sobre la muestra y evalúala en una cuadrícula:
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
import numpy as np
from scipy.stats import gaussian_kde
data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
density = gaussian_kde(data)
x = np.linspace(-2, 10, 300)
y = density(x)
print(f"Peak estimated density: {y.max():.4f} at x = {x[np.argmax(y)]:.2f}")
Peak estimated density: 0.2635 at x = 3.14
El programa ajusta gaussian_kde sobre los 18 valores de la muestra y evalúa la estimación en 300 posiciones espaciadas uniformemente entre -2 y 10; la estimación alcanza su máximo cerca de x = 3, donde se concentran la mayoría de las observaciones. Elige la cuadrícula a partir del rango de tus propios datos, por ejemplo desde un poco por debajo del mínimo hasta un poco por encima del máximo; la ventana de -2 a 10 de aquí solo es adecuada para esta muestra.
El script de trazado siguiente fija el backend no interactivo Agg de Matplotlib para que se ejecute igual con o sin pantalla conectada, y silencia el registro de la biblioteca para que un mensaje de fuentes de la primera ejecución no aparezca en la salida de error estándar. Ejecuta esto para ver qué backend está seleccionado:
import logging
import matplotlib
matplotlib.use("Agg")
logging.getLogger("matplotlib").setLevel(logging.ERROR)
import matplotlib.pyplot as plt
print("Backend:", matplotlib.get_backend())
Backend: Agg
Ahora dibuja la curva evaluada con el script completo:
import logging
import matplotlib
matplotlib.use("Agg")
logging.getLogger("matplotlib").setLevel(logging.ERROR)
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
density = gaussian_kde(data)
x = np.linspace(-2, 10, 300)
y = density(x)
plt.plot(x, y)
plt.title("Density Plot of the data")
plt.xlabel("Value")
plt.ylabel("Estimated density")
print("Plotted", len(plt.gca().get_lines()), "density line")
Plotted 1 density line
plt.plot() dibuja la densidad estimada como una única línea en los ejes actuales, y plt.gca().get_lines() informa de la única línea que añadió. En una sesión interactiva puedes omitir la llamada matplotlib.use("Agg") y añadir plt.show() después de las llamadas de trazado para abrir la ventana de la figura.
La curva representada es la densidad estimada: más alta donde las observaciones se concentran y más baja donde son escasas. Como se trata de una estimación, interpreta su forma como una vista de esta muestra y no como una medida de la distribución verdadera.
Ajustar el ancho de banda de KDE
El ancho de banda controla cuánto se suaviza la estimación. Un ancho de banda grande ensancha cada núcleo y produce una curva más plana y suave que puede ocultar estructura real; un ancho de banda pequeño estrecha los núcleos, de modo que la curva sigue de cerca la muestra y puede mostrar ondulaciones que solo son ruido en este conjunto de datos concreto. De forma predeterminada, gaussian_kde elige el ancho de banda con la regla de Scott; también puedes pasar "scott" o "silverman" explícitamente, o suministrar un escalar mediante el argumento bw_method, que fija el ancho del núcleo en relación con la dispersión de los datos. El mismo ajuste está disponible más tarde a través del método público set_bandwidth(), por lo que no es necesario llamar a funciones auxiliares privadas.
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
density = gaussian_kde(data, bw_method=0.25)
x = np.linspace(-2, 10, 300)
y = density(x)
plt.plot(x, y)
plt.title("Density Plot with a smaller bandwidth")
plt.xlabel("Value")
plt.ylabel("Estimated density")
print(f"Peak estimated density: {y.max():.4f}")
Peak estimated density: 0.3913
Con bw_method=0.25 cada núcleo es más estrecho que el predeterminado, por lo que la curva se eleva más en su pico y sigue más de cerca la concentración de la muestra. Sube el valor para suavizar más, bájalo para suavizar menos, y compara las formas en lugar de tratar un ancho de banda concreto como universalmente correcto.
Crear un gráfico de densidad con Seaborn
seaborn envuelve la misma idea en una sola llamada: kdeplot() estima la densidad internamente y la dibuja en unos ejes de Matplotlib. Pasa la muestra a través de x y escala el ancho de banda elegido automáticamente con bw_adjust: los valores superiores a 1 suavizan más y los inferiores a 1 suavizan menos. La función antigua distplot() está obsoleta; usa kdeplot() para la curva sola o displot(kind="kde") para la variante a nivel de figura.
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import seaborn as sns
data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
ax = sns.kdeplot(x=data, bw_adjust=0.5)
ax.set_title("Density Plot of the data")
ax.set_xlabel("Value")
print("kdeplot drew", len(ax.get_lines()), "density line")
kdeplot drew 1 density line
kdeplot devuelve los Axes de Matplotlib sobre los que dibujó, por lo que los títulos y las etiquetas se aplican con los métodos habituales de los ejes; añade plt.show() para mostrar la ventana de la figura de forma interactiva.
Crear un gráfico de densidad con pandas
Cuando la muestra ya está en un DataFrame o una Series, pandas.DataFrame.plot puede producir la curva directamente: pasa kind="density" (el alias kind="kde" también funciona). pandas calcula la estimación con gaussian_kde, por lo que SciPy debe estar instalado para este tipo de gráfico.
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd
data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
df = pd.DataFrame(data, columns=["value"])
ax = df.plot(kind="density")
ax.set_title("Density Plot of the data")
ax.set_xlabel("Value")
print("pandas labeled the axis:", ax.get_ylabel())
pandas labeled the axis: Density
Al llamar al método sobre un DataFrame se traza una curva por columna y el eje y se etiqueta automáticamente como Density; llámalo sobre una sola Series para una variable con nombre. Al igual que con el enfoque de SciPy, la suavidad sigue el ancho de banda del estimador, que pandas deriva de los datos.