Comment créer un graphique de densité dans matplotlib?

122

Dans RI peut créer la sortie souhaitée en faisant:

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))

Graphique de densité en R

En python (avec matplotlib), le plus proche que j'ai obtenu était avec un simple histogramme:

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()

Histogramme dans matplotlib

J'ai également essayé le paramètre normed = True mais je n'ai rien pu obtenir d'autre que d'essayer d'adapter un gaussien à l'histogramme.

Mes dernières tentatives étaient autour scipy.statset gaussian_kde, suivant des exemples sur le Web, mais j'ai échoué jusqu'à présent.

unode
la source
Jetez un œil à seaborn stackoverflow.com/a/32803224/1922302
johk95

Réponses:

124

Sven a montré comment utiliser la classe gaussian_kdede Scipy, mais vous remarquerez qu'elle ne ressemble pas tout à fait à ce que vous avez généré avec R. C'est parce qu'il gaussian_kdeessaie de déduire automatiquement la bande passante. Vous pouvez jouer avec la bande passante d'une certaine manière en modifiant la fonction covariance_factorde la gaussian_kdeclasse. Tout d'abord, voici ce que vous obtenez sans changer cette fonction:

texte alternatif

Cependant, si j'utilise le code suivant:

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()

Je reçois

texte alternatif

ce qui est assez proche de ce que vous obtenez de R. Qu'ai-je fait? gaussian_kdeutilise une fonction modifiable covariance_factorpour calculer sa bande passante. Avant de modifier la fonction, la valeur renvoyée par covariance_factor pour ces données était d'environ 0,5. La réduction de cela a réduit la bande passante. J'ai dû appeler _compute_covarianceaprès avoir changé cette fonction pour que tous les facteurs soient calculés correctement. Ce n'est pas une correspondance exacte avec le paramètre bw de R, mais j'espère que cela vous aidera à aller dans la bonne direction.

Justin Peel
la source
6
@Justin Bonne réponse (+1) et ne voulant pas lancer de guerre de flamme Python v R ou quoi que ce soit, mais j'aime la façon dont R fonctionne avec des données beaucoup plus succinctement que python et d'autres langages. Je suis sûr que python a beaucoup de bons points sur R (je ne suis pas un utilisateur de Python donc je suis tellement en uniforme pour éventuellement commenter) et peut être utilisé pour beaucoup plus de travail que l'analyse de données, mais en tant que R de longue date utilisateur J'oublie à quel point un langage est succinct pour de telles tâches jusqu'à ce que des exemples comme celui-ci apparaissent.
Gavin Simpson
4
(toujours en train de se battre avec l'édition des commentaires) Voici une sous-classe de gaussian_kde qui permet de définir la bande passante comme argument et plus d'exemples: mail.scipy.org/pipermail/scipy-user/2010-January/023877.html et il y a une amélioration ticket à projects.scipy.org/scipy/ticket/1092 . Notez que gaussian_kde est conçu pour les données à n dimensions.
Josef
11
@Gavin Simpson, oui, R est plus succinct car il a une portée plus étroite. Il est fait pour le calcul statistique et les graphiques. Python est un langage de programmation général qui peut faire à peu près tout ce que vous voulez qu'il fasse. Pour cette raison, la syntaxe peut ne pas être aussi succincte. Une partie de cela est une conception différente dans Numpy / Scipy, mais une partie est juste la configuration modulaire sur Python. R est génial si vous avez seulement besoin de faire des calculs et des graphiques, mais si vous avez besoin d'utiliser ces calculs dans certaines applications de brader, vous voudrez peut-être quelque chose comme Python. Cependant, vous pouvez également utiliser R de Python ...
Justin Peel
10
Une set_bandwidthméthode et un bw_methodargument de constructeur ont été ajoutés à gaussian_kde dans scipy 0.11.0 par numéro 1619
eddygeek
1
réponse obsolète. Voir ci-dessous la solution Seaborn, qui est désormais plus standard en Python.
LudvigH
148

Cinq ans plus tard, quand je Google "comment créer un graphique de densité de noyau en utilisant python", ce fil apparaît toujours en haut!

Aujourd'hui, un moyen beaucoup plus simple de le faire est d'utiliser seaborn , un package qui fournit de nombreuses fonctions de traçage pratiques et une bonne gestion du style.

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)

entrez la description de l'image ici

Xin
la source
Merci beaucoup .. Vous recherchez quelque chose comme ça depuis des jours .. pouvez-vous expliquer pourquoi il bw=0.5est donné?
Sitz Blogz
4
@SitzBlogz Le bwparamètre représente la bande passante. J'essayais de faire correspondre le réglage de OP (voir son premier exemple de code d'origine). Pour une explication détaillée des bwcontrôles, voir en.wikipedia.org/wiki/… . Fondamentalement, il contrôle la fluidité souhaitée du tracé de densité. Plus le pc est grand, plus il sera lisse.
Xin
J'ai une autre question à demander à mes données étant de nature discrète et j'essaie de tracer le PDF pour cela, après avoir lu scipy doc, j'ai compris que PMF = PDF des suggestions à ce sujet comment le tracer?
Sitz Blogz
1
Quand j'essaye ceci, j'obtiensTypeError: slice indices must be integers or None or have an __index__ method
endolith
48

Option 1:

Utilisez le pandastracé de dataframe (construit au-dessus de 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()

entrez la description de l'image ici

Option 2:

Utilisation 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)

entrez la description de l'image ici

Aziz Alto
la source
4
Pour ajouter le paramètre de bande passante: df.plot.density (bw_method = 0.5)
Anake
3
@Aziz Pas besoin pandas.DataFrame, peut utiliser pandas.Series(data).plot(kind='density')@Anake, pas besoin de définir df.plot.density comme étape séparée; peut simplement passer dans votre bw_methodkwarg danspd.Series(data).plot(kind='density', bw_method=0.5)
The Red Pea
45

Essayez peut-être quelque chose comme:

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()

Vous pouvez facilement le remplacer gaussian_kde()par une autre estimation de la densité du noyau.

Sven Marnach
la source
0

Le tracé de densité peut également être créé en utilisant matplotlib: La fonction plt.hist (data) renvoie les valeurs y et x nécessaires au tracé de densité (voir la documentation https://matplotlib.org/3.1.1/api/_as_gen/ matplotlib.pyplot.hist.html ). En conséquence, le code suivant crée un graphique de densité à l'aide de la bibliothèque 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])      

Ce code renvoie le graphique de densité suivant

entrez la description de l'image ici

tetrisforjeff
la source