¿Cómo crear una gráfica de densidad en matplotlib?

122

En RI puede crear la salida deseada haciendo:

data = c(rep(1.5, 7), rep(2.5, 2), rep(3.5, 8),
         rep(4.5, 3), rep(5.5, 1), rep(6.5, 8))
plot(density(data, bw=0.5))

Gráfico de densidad en R

En python (con matplotlib) lo más cercano que pude fue con un histograma simple:

import matplotlib.pyplot as plt
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
plt.hist(data, bins=6)
plt.show()

Histograma en matplotlib

También probé el parámetro normed = True pero no pude obtener nada más que intentar ajustar un gaussiano al histograma.

Mis últimos intentos estuvieron presentes scipy.statsy gaussian_kde, siguiendo ejemplos en la web, pero hasta ahora no he tenido éxito.

unode
fuente
Eche un vistazo a seaborn stackoverflow.com/a/32803224/1922302
johk95

Respuestas:

124

Sven ha mostrado cómo usar la clase gaussian_kdede Scipy, pero notarás que no se parece mucho a lo que generaste con R. Esto se debe a que gaussian_kdeintenta inferir el ancho de banda automáticamente. Puede jugar con el ancho de banda de alguna manera cambiando la función covariance_factorde la gaussian_kdeclase. Primero, esto es lo que obtiene sin cambiar esa función:

texto alternativo

Sin embargo, si utilizo el siguiente código:

import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
density = gaussian_kde(data)
xs = np.linspace(0,8,200)
density.covariance_factor = lambda : .25
density._compute_covariance()
plt.plot(xs,density(xs))
plt.show()

yo obtengo

texto alternativo

que está bastante cerca de lo que está obteniendo de R. ¿Qué he hecho? gaussian_kdeutiliza una función modificable covariance_factorpara calcular su ancho de banda. Antes de cambiar la función, el valor devuelto por covariance_factor para estos datos era aproximadamente .5. Bajar esto redujo el ancho de banda. Tuve que llamar _compute_covariancedespués de cambiar esa función para que todos los factores se calcularan correctamente. No es una correspondencia exacta con el parámetro bw de R, pero es de esperar que le ayude a ir en la dirección correcta.

Justin Peel
fuente
6
@Justin Buena respuesta (+1) y no quiero iniciar ninguna guerra de llamas Python v R ni nada, pero me encanta la forma en que R trabaja con datos de manera mucho más sucinta que Python y otros lenguajes. Estoy seguro de que Python tiene muchos puntos buenos sobre R (no soy un usuario de Python, así que estoy totalmente uniformado para posiblemente comentar) y se puede usar para mucho más trabajo que analizar datos, pero como un R desde hace mucho tiempo. usuario Olvidé lo sucinto que es el lenguaje para tales tareas hasta que surjan ejemplos como este.
Gavin Simpson
4
(todavía luchando con la edición de comentarios) Aquí hay una subclase de gaussian_kde que permite establecer el ancho de banda como argumento y más ejemplos: mail.scipy.org/pipermail/scipy-user/2010-January/023877.html y hay una mejora ticket en projects.scipy.org/scipy/ticket/1092 . Tenga en cuenta que gaussian_kde está diseñado para datos n-dimensionales.
Josef
11
@Gavin Simpson, sí, R es más conciso porque tiene un alcance más estrecho. Está hecho para cálculos estadísticos y gráficos. Python es un lenguaje de programación general que puede hacer prácticamente todo lo que quieras que haga. Por eso, la sintaxis podría no ser tan sucinta. Parte de eso es un diseño diferente en Numpy / Scipy, pero parte de él es solo la configuración modular en Python. R es genial si solo necesita hacer cálculos y gráficos, pero si necesita usar esos cálculos en alguna aplicación de brader, entonces es posible que desee algo como Python. Sin embargo, también puede usar R de Python ...
Justin Peel
10
Se agregaron un set_bandwidthmétodo y un bw_methodargumento de constructor a gaussian_kde en scipy 0.11.0 por número 1619
eddygeek
1
respuesta obsoleta. Vea a continuación la solución Seaborn, que ahora es más estándar en Python.
LudvigH
148

Cinco años después, cuando busqué en Google "cómo crear una gráfica de densidad del kernel usando Python", ¡este hilo todavía aparece en la parte superior!

Hoy en día, una forma mucho más fácil de hacer esto es utilizar seaborn , un paquete que proporciona muchas funciones de trazado convenientes y una buena gestión de estilo.

import numpy as np
import seaborn as sns
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
sns.set_style('whitegrid')
sns.kdeplot(np.array(data), bw=0.5)

ingrese la descripción de la imagen aquí

Xin
fuente
Muchas gracias ... He estado buscando algo como esto desde hace días ... ¿pueden explicar por qué bw=0.5se da?
Sitz Blogz
4
@SitzBlogz El bwparámetro significa ancho de banda. Estaba tratando de hacer coincidir la configuración de OP (vea su primer ejemplo de código original). Para obtener una explicación detallada de los bwcontroles, consulte en.wikipedia.org/wiki/… . Básicamente, controla qué tan suave desea que sea la gráfica de densidad. Cuanto mayor sea el peso corporal, más suave será.
Xin
Tengo otra consulta para preguntar que mis datos son de naturaleza discreta y estoy tratando de trazar el PDF para eso, después de leer scipy doc, entendí que PMF = PDF ¿alguna sugerencia sobre cómo trazarlo?
Sitz Blogz
1
Cuando intento esto, obtengoTypeError: slice indices must be integers or None or have an __index__ method
endolito
48

Opción 1:

Utilice el pandasdiagrama de marco de datos (construido sobre matplotlib):

import pandas as pd
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
pd.DataFrame(data).plot(kind='density') # or pd.Series()

ingrese la descripción de la imagen aquí

Opcion 2:

Uso distplotde seaborn:

import seaborn as sns
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
sns.distplot(data, hist=False)

ingrese la descripción de la imagen aquí

Aziz Alto
fuente
4
Para agregar el parámetro de ancho de banda: df.plot.density (bw_method = 0.5)
Anake
3
@Aziz No es necesario pandas.DataFrame, puede usar pandas.Series(data).plot(kind='density')@Anake, no es necesario configurar df.plot.density como un paso separado; puede pasar en su bw_methodkwarg apd.Series(data).plot(kind='density', bw_method=0.5)
The Red Pea
45

Tal vez intente algo como:

import matplotlib.pyplot as plt
import numpy
from scipy import stats
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
density = stats.kde.gaussian_kde(data)
x = numpy.arange(0., 8, .1)
plt.plot(x, density(x))
plt.show()

Puede reemplazarlo fácilmente gaussian_kde()por una estimación de densidad de kernel diferente.

Sven Marnach
fuente
0

La gráfica de densidad también se puede crear usando matplotlib: La función plt.hist (data) devuelve los valores yyx necesarios para la gráfica de densidad (consulte la documentación https://matplotlib.org/3.1.1/api/_as_gen/ matplotlib.pyplot.hist.html ). Como resultado, el siguiente código crea una gráfica de densidad utilizando la biblioteca matplotlib:

import matplotlib.pyplot as plt
dat=[-1,2,1,4,-5,3,6,1,2,1,2,5,6,5,6,2,2,2]
a=plt.hist(dat,density=True)
plt.close()
plt.figure()
plt.plot(a[1][1:],a[0])      

Este código devuelve la siguiente gráfica de densidad

ingrese la descripción de la imagen aquí

tetrisforjeff
fuente