Como criar um gráfico de densidade em matplotlib?

122

No RI, é possível criar a saída desejada fazendo:

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 densidade em R

Em python (com matplotlib), o mais próximo que cheguei foi com um histograma simples:

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 em matplotlib

Eu também tentei o parâmetro normed = True, mas não consegui nada além de tentar ajustar um gaussiano ao histograma.

Minhas últimas tentativas foram por aí scipy.statse gaussian_kde, seguindo exemplos na web, mas não tive sucesso até agora.

unode
fonte
Dê uma olhada em seaborn stackoverflow.com/a/32803224/1922302
johk95

Respostas:

124

Sven mostrou como usar a classe gaussian_kdede Scipy, mas você notará que não se parece muito com o que você gerou com R. Isso ocorre porque gaussian_kdetenta inferir a largura de banda automaticamente. Você pode brincar com a largura de banda mudando a função covariance_factorda gaussian_kdeclasse. Primeiro, aqui está o que você obtém sem alterar essa função:

texto alternativo

No entanto, se eu usar o seguinte 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()

eu recebo

texto alternativo

o que é bem parecido com o que você está recebendo da R. O que eu fiz? gaussian_kdeusa uma função mutável, covariance_factorpara calcular sua largura de banda. Antes de alterar a função, o valor retornado por covariance_factor para esses dados era de cerca de 0,5. Reduzir isso reduziu a largura de banda. Tive que chamar _compute_covariancedepois de alterar essa função para que todos os fatores fossem calculados corretamente. Não é uma correspondência exata com o parâmetro bw de R, mas espero que ajude você a ir na direção certa.

Justin Peel
fonte
6
@Justin Boa resposta (+1) e não querendo iniciar nenhuma guerra flamejante do Python v R nem nada, mas estou adorando a maneira como o R trabalha com dados de forma muito mais sucinta que o python e outras linguagens. Tenho certeza de que python tem muitos pontos positivos em relação ao R (não sou um usuário de Python, então estou totalmente uniformizado para possivelmente comentar) e pode ser usado para muito mais trabalho do que analisar dados, mas como um R de longa data user Eu esqueci como é uma linguagem sucinta para tais tarefas até que exemplos como este apareçam.
Gavin Simpson
4
(ainda lutando com a edição de comentários) Aqui está uma subclasse de gaussian_kde que permite definir a largura de banda como um argumento e mais exemplos: mail.scipy.org/pipermail/scipy-user/2010-January/023877.html e há um aprimoramento bilhete em projects.scipy.org/scipy/ticket/1092 . Observe, gaussian_kde é projetado para dados n-dimensionais.
Josef
11
@Gavin Simpson, sim, R é mais sucinto porque tem um escopo mais estreito. É feito para computação estatística e gráficos. Python é uma linguagem de programação geral que pode fazer praticamente tudo o que você quiser. Por causa disso, a sintaxe pode não ser tão sucinta. Parte disso é um design diferente em Numpy / Scipy, mas parte é apenas a configuração modular em Python. R é ótimo se você só precisa fazer cálculos e gráficos, mas se precisar usar esses cálculos em algum aplicativo de brader, então você pode querer algo como Python. No entanto, você também pode usar R do Python ...
Justin Peel
10
Um set_bandwidthmétodo e um bw_methodargumento de construtor foram adicionados a gaussian_kde no scipy 0.11.0 por edição de 1619
eddygeek
1
resposta desatualizada. Veja abaixo a solução Seaborn, que agora é mais padrão em Python.
LudvigH
148

Cinco anos depois, quando pesquisei "como criar um gráfico de densidade do kernel usando python" no Google, esse tópico ainda aparece no topo!

Hoje, uma maneira muito mais fácil de fazer isso é usar o seaborn , um pacote que oferece muitas funções convenientes de plotagem e bom gerenciamento 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)

insira a descrição da imagem aqui

Xin
fonte
Muito obrigado .. Estive procurando por algo assim desde dias .. u pls pode explicar porque bw=0.5é dado?
Sitz Blogz
4
@SitzBlogz O bwparâmetro significa largura de banda. Eu estava tentando corresponder à configuração de OP (veja seu primeiro exemplo de código original). Para uma explicação detalhada sobre quais bwcontroles, consulte en.wikipedia.org/wiki/… . Basicamente, ele controla o quão suave você deseja que o gráfico de densidade seja. Quanto maior for o bw, mais suave será.
Xin de
Tenho outra consulta para pedir que meus dados sejam de natureza discreta e estou tentando traçar o PDF para isso, depois de ler o documento scipy entendi que PMF = PDF alguma sugestão sobre como traçá-lo?
Sitz Blogz
1
Quando tento fazer isso, consigoTypeError: slice indices must be integers or None or have an __index__ method
endolith
48

Opção 1:

Use pandasdataframe plot (construído 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()

insira a descrição da imagem aqui

Opção 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)

insira a descrição da imagem aqui

Aziz Alto
fonte
4
Para adicionar o parâmetro de largura de banda: df.plot.density (bw_method = 0.5)
Anake
3
@Aziz Não precisa pandas.DataFrame, pode usar pandas.Series(data).plot(kind='density')@Anake, não precisa definir df.plot.density como uma etapa separada; pode simplesmente passar seu bw_methodkwarg parapd.Series(data).plot(kind='density', bw_method=0.5)
The Red Pea
45

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

Você pode facilmente substituir gaussian_kde()por uma estimativa de densidade de kernel diferente.

Sven Marnach
fonte
0

O gráfico de densidade também pode ser criado usando matplotlib: A função plt.hist (dados) retorna os valores y e x necessários para o gráfico de densidade (consulte a documentação https://matplotlib.org/3.1.1/api/_as_gen/ matplotlib.pyplot.hist.html ). Consequentemente, o código a seguir cria um gráfico de densidade usando a 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 retorna o seguinte gráfico de densidade

insira a descrição da imagem aqui

tetrisforjeff
fonte