HOWTO · Matplotlib
Gráfico de densidade Matplotlib
Para gerar um gráfico de densidade usando Python, primeiro estimamos a função de densidade a partir dos dados fornecidos usando o método gaussian_kde() do módulo scipy.stats e depois traçamos a função de densidade para gerar o gráfico de densidade.
Nesta página
Um gráfico de densidade mostra uma estimativa suavizada de como os dados de uma amostra unidimensional estão distribuídos. Em Python, a via habitual é a estimativa de densidade por núcleo (KDE): estime uma função de densidade a partir das observações com gaussian_kde() do módulo scipy.stats, avalie-a numa grelha de valores de x e desenhe a curva resultante com o Matplotlib. Pode obter o mesmo resultado com menos código através de kdeplot() do pacote seaborn ou definindo kind="density" em pandas.DataFrame.plot(). Os exemplos abaixo utilizam NumPy, SciPy e Matplotlib; as variantes com Seaborn e pandas precisam adicionalmente desses pacotes instalados, e o gráfico do pandas requer o SciPy porque chama gaussian_kde internamente.
Crie um gráfico de densidade com scipy.stats.gaussian_kde
gaussian_kde constrói a estimativa colocando um núcleo — uma pequena protuberância com a forma de uma gaussiana — sobre cada observação e calculando a média dessas protuberâncias. O resultado é uma função de densidade invocável: passe-lhe uma grelha de posições de x e ela devolve a densidade estimada em cada posição, que é exatamente a linha que o Matplotlib desenha.
O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.
Primeiro, ajuste a estimativa à amostra e avalie-a numa grelha:
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
O programa ajusta gaussian_kde aos 18 valores da amostra e avalia a estimativa em 300 posições igualmente espaçadas entre -2 e 10; a estimativa atinge o pico perto de x = 3, onde a maioria das observações se concentra. Escolha a grelha a partir do intervalo dos seus próprios dados, por exemplo de um pouco abaixo do mínimo até um pouco acima do máximo; a janela de -2 a 10 aqui serve apenas esta amostra.
O script de desenho abaixo fixa o backend não interativo Agg do Matplotlib para que seja executado da mesma forma com ou sem um ecrã ligado, e silencia o registo da biblioteca para que uma mensagem de tipos de letra na primeira execução não apareça na saída de erro padrão. Execute o seguinte para ver qual é o backend selecionado:
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
Agora desenhe a curva avaliada com o 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() desenha a densidade estimada como uma única linha nos eixos atuais, e plt.gca().get_lines() reporta a linha que foi adicionada. Numa sessão interativa, pode omitir a chamada matplotlib.use("Agg") e adicionar plt.show() após as chamadas de desenho para abrir a janela da figura.
A curva desenhada é a densidade estimada: mais alta onde as observações estão concentradas, mais baixa onde são escassas. Por se tratar de uma estimativa, encare a sua forma como uma visão desta amostra e não como uma medição da distribuição verdadeira.
Ajuste a largura de banda do KDE
A largura de banda controla quanto a estimativa é suavizada. Uma largura de banda grande alarga cada núcleo e produz uma curva mais plana e suave que pode ocultar estrutura real; uma largura de banda pequena estreita os núcleos, pelo que a curva segue a amostra de perto e pode apresentar ondulações que são apenas ruído neste conjunto de dados em particular. Por predefinição, gaussian_kde escolhe a largura de banda com a regra de Scott; também pode passar "scott" ou "silverman" explicitamente, ou fornecer um escalar através do argumento bw_method, que define a largura do núcleo em relação à dispersão dos dados. O mesmo ajuste fica disponível mais tarde através do método público set_bandwidth(), pelo que não é necessário chamar auxiliares privados.
O código e os rótulos ou saídas exibidos nesta seção permanecem em 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
Com bw_method=0.25, cada núcleo é mais estreito do que na predefinição, pelo que a curva sobe mais no seu pico e acompanha mais de perto a concentração da amostra. Aumente o valor para suavizar mais, reduza-o para suavizar menos e compare as formas, em vez de tratar qualquer largura de banda específica como universalmente correta.
Crie um gráfico de densidade com o Seaborn
seaborn encapsula a mesma ideia numa única chamada: kdeplot() estima a densidade internamente e desenha-a num eixo do Matplotlib. Passe a amostra através de x e ajuste a escala da largura de banda escolhida automaticamente com bw_adjust — valores acima de 1 suavizam mais, valores abaixo de 1 suavizam menos. A função mais antiga distplot() está descontinuada; utilize kdeplot() para a curva isolada ou displot(kind="kde") para a variante ao nível da figura.
O código e os rótulos ou saídas exibidos nesta seção permanecem em 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 devolve o Axes do Matplotlib em que desenhou, pelo que os títulos e rótulos são aplicados com os métodos habituais dos eixos; adicione plt.show() para apresentar a janela da figura de forma interativa.
Crie um gráfico de densidade com o pandas
Quando a amostra já está num DataFrame ou numa Series, pandas.DataFrame.plot pode produzir a curva diretamente: passe kind="density" (o alias kind="kde" também funciona). O pandas calcula a estimativa com gaussian_kde, pelo que o SciPy tem de estar instalado para este tipo de gráfico.
O código e os rótulos ou saídas exibidos nesta seção permanecem em 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
Chamar o método num DataFrame desenha uma curva por coluna e rotula automaticamente o eixo y como Density, um rótulo que permanece em inglês; chame-o sobre uma única Series para uma variável nomeada. Tal como na abordagem com SciPy, a suavidade segue a largura de banda do estimador, que o pandas deriva dos dados.