Python Data Science Handbook - Jake VanderPlas (254-354)
Python Data Science Handbook - Jake VanderPlas (254-354)
Observe que o argumento color é automaticamente mapeado para uma escala de cores
(mostrada aqui pelo comando colorbar() ), e o argumento size é fornecido em pixels. Desta
forma, a cor e o tamanho dos pontos podem ser utilizados para transmitir informações na
visualização, a fim de ilustrar dados multidimensionais.
Por exemplo, podemos usar os dados Iris do Scikit-Learn, onde cada amostra é um dos três
tipos de flores cujo tamanho de pétalas e sépalas foi cuidadosamente medido (Figura 4-26) :
Figura 4-26. Usando propriedades de ponto para codificar recursos dos dados Iris
Podemos ver que este gráfico de dispersão nos deu a capacidade de explorar simultaneamente quatro
dimensões diferentes dos dados: a localização (x, y) de cada ponto corresponde ao comprimento e
largura da sépala, o tamanho do ponto está relacionado à pétala largura e a cor está relacionada à
espécie específica de flor. Gráficos de dispersão multicoloridos e com vários recursos como este podem
ser úteis tanto para exploração quanto para apresentação de dados.
dos diferentes recursos disponíveis em plt.plot e plt.scatter, por que você escolheria usar um em vez do
outro? Embora isso não importe tanto para pequenas quantidades de dados, à medida que os conjuntos
de dados ficam maiores do que alguns milhares de pontos, plt.plot pode ser visivelmente mais eficiente
que plt.scatter. A razão é que plt.scatter tem a capacidade de renderizar um tamanho e/ou cor diferente
para cada ponto, então o renderizador deve fazer o trabalho extra de construir cada ponto individualmente.
Em plt.plot, por outro lado, os pontos são sempre essencialmente clones uns dos outros, portanto o
trabalho de determinar a aparência dos pontos é feito apenas uma vez para todo o conjunto de dados.
Para grandes conjuntos de dados, a diferença entre os dois pode levar a um desempenho muito diferente
e, por esse motivo, plt.plot deve ser preferido a plt.scatter para grandes conjuntos de dados.
Visualizando Erros
Para qualquer medição científica, a contabilização precisa dos erros é quase tão importante, se não mais
importante, do que o relato preciso do próprio número. Por exemplo, imagine que estou a utilizar algumas
observações astrofísicas para estimar a Constante de Hubble, a medida local da taxa de expansão do
Universo. Sei que a literatura atual sugere um valor em torno de 71 (km/s)/Mpc, e meço um valor de 74
(km/s)/Mpc com meu método. Os valores são consistentes? A única resposta correta, dada esta
informação, é esta: não há como saber.
Suponha que eu aumente esta informação com incertezas relatadas: a literatura atual sugere um valor de cerca
de 71 ± 2,5 (km/s)/Mpc, e meu método mediu um valor de 74 ± 5 (km/s)/Mpc. Agora os valores são consistentes?
Essa é uma questão que pode ser respondida quantitativamente.
Na visualização de dados e resultados, mostrar esses erros de forma eficaz pode fazer com que um gráfico
transmita informações muito mais completas.
Uma barra de erros básica pode ser criada com uma única chamada de função Matplotlib (Figura 4-27):
Em [1]: % matplotlib
importação inline matplotlib.pyplot
como plt plt.style.use('seaborn-whitegrid')
importação numpy como np
Aqui, o fmt é um código de formato que controla a aparência de linhas e pontos e tem a mesma sintaxe da
abreviação usada em plt.plot, descrita em “Gráficos de Linha Simples” na página 224 e “Gráficos de Dispersão
Simples” na página 233.
Além dessas opções básicas, a função errorbar possui muitas opções para ajustar as saídas. Usando essas
opções adicionais, você pode personalizar facilmente a estética do gráfico da sua barra de erros. Muitas vezes
acho útil, especialmente em gráficos lotados, deixar as barras de erro mais claras que os próprios pontos (Figura
4-28):
Além dessas opções, você também pode especificar barras de erro horizontais (xerr), barras
de erro unilaterais e muitas outras variantes. Para obter mais informações sobre as opções
disponíveis, consulte a documentação de plt.errorbar.
Erros Contínuos
Em algumas situações é desejável mostrar barras de erro em quantidades contínuas. Embora
o Matplotlib não tenha uma rotina de conveniência integrada para esse tipo de aplicativo, é
relativamente fácil combinar primitivos como plt.plot e plt.fill_between para obter um resultado
útil.
Aqui realizaremos uma regressão de processo gaussiana simples (GPR), usando a API Scikit-
Learn (consulte “Apresentando o Scikit-Learn” na página 343 para obter detalhes). Este é um
método para ajustar uma função não paramétrica muito flexível a dados com uma medida
contínua da incerteza. Não nos aprofundaremos nos detalhes da regressão do processo
gaussiano neste momento, mas nos concentraremos em como você pode visualizar um erro tão contínuo
medição:
Agora temos xfit, yfit e dyfit, que mostram o ajuste contínuo aos nossos dados. Poderíamos
passá-los para a função plt.errorbar como acima, mas na verdade não queremos plotar 1.000
pontos com 1.000 barras de erro. Em vez disso, podemos usar a função plt.fill_between com
uma cor clara para visualizar esse erro contínuo (Figura 4-29):
Observe o que fizemos aqui com a função fill_between : passamos um valor x, depois o limite
inferior de y, depois o limite superior de y, e o resultado é que a área entre essas regiões é
preenchida.
A figura resultante dá uma visão muito intuitiva do que o algoritmo de regressão do processo
gaussiano está fazendo: em regiões próximas a um ponto de dados medido, o modelo é
fortemente restrito e isso se reflete nos pequenos erros do modelo. Em regiões distantes de
um ponto de dados medido, o modelo não é fortemente restringido e os erros do modelo
aumentam.
Finalmente, se isso parece um nível um pouco baixo para o seu gosto, consulte “Visualização
com Seaborn” na página 311, onde discutimos o pacote Seaborn, que possui uma API mais
simplificada para visualizar esse tipo de barra de erro contínua.
Em [1]: % matplotlib
importação inline matplotlib.pyplot
como plt plt.style.use('seaborn-
white') importação numpy como np
Um gráfico de contorno pode ser criado com a função plt.contour . São necessários três argumentos:
uma grade de valores x, uma grade de valores y e uma grade de valores z. Os valores xey representam
posições no gráfico e os valores z serão representados pelos níveis de contorno. Talvez a maneira mais
direta de preparar esses dados seja usar a função np.meshgrid , que constrói grades bidimensionais a
partir de arrays unidimensionais:
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
Agora vamos ver isso com um gráfico de contorno padrão somente de linha (Figura 4-30):
Observe que, por padrão, quando uma única cor é usada, os valores negativos são representados por
linhas tracejadas e os valores positivos por linhas sólidas. Alternativamente, você pode codificar as linhas
com cores especificando um mapa de cores com o argumento cmap . Aqui também especificaremos que
queremos que mais linhas sejam desenhadas – 20 intervalos igualmente espaçados dentro do intervalo
de dados (Figura 4-31):
Aqui escolhemos o mapa de cores RdGy (abreviação de Red-Gray), que é uma boa escolha para dados
centralizados. Matplotlib tem uma ampla variedade de mapas de cores disponíveis, que você pode navegar
facilmente no IPython completando uma guia no módulo plt.cm :
plt.cm.<TAB>
Nosso enredo parece melhor, mas os espaços entre as linhas podem distrair um pouco.
Podemos mudar isso mudando para um gráfico de contorno preenchido usando a função plt.contourf()
(observe o f no final), que usa basicamente a mesma sintaxe de plt.con tour().
Além disso, adicionaremos um comando plt.colorbar() , que cria automaticamente um eixo adicional
com informações de cores rotuladas para o gráfico (Figura 4-32):
A barra de cores deixa claro que as regiões pretas são “picos”, enquanto as regiões vermelhas são
“vales”.
Um problema potencial com este enredo é que ele é um pouco “manchado”. Ou seja, as etapas de
cores são discretas e não contínuas, o que nem sempre é o desejado. Você poderia remediar isso
definindo o número de contornos para um número muito alto, mas isso resulta em um gráfico
bastante ineficiente: o Matplotlib deve renderizar um novo polígono para cada etapa do nível. A
melhor maneira de lidar com isso é usar a função plt.imshow() , que interpreta uma grade
bidimensional de dados como uma imagem.
• plt.imshow() não aceita uma grade xey, então você deve especificar manualmente a
extensão [xmin, xmax, ymin, ymax] da imagem no gráfico. •
plt.imshow() por padrão segue a definição padrão de array de imagens onde a origem está
no canto superior esquerdo, e não no canto inferior esquerdo como na maioria dos gráficos
de contorno. Isso deve ser alterado ao mostrar dados em grade.
• plt.imshow() ajustará automaticamente a proporção do eixo para corresponder aos dados de entrada;
você pode alterar isso definindo, por exemplo, plt.axis(aspect='image') para fazer com que as
unidades x e y correspondam.
Finalmente, às vezes pode ser útil combinar gráficos de contorno e gráficos de imagem. Por exemplo,
para criar o efeito mostrado na Figura 4-34, usaremos uma imagem de fundo parcialmente transparente
(com transparência definida por meio do parâmetro alfa ) e plotaremos sobre os contornos com rótulos
nos próprios contornos (usando o comando plt.clabel( ) função):
A combinação dessas três funções – plt.contour, plt.contourf e plt.imshow – oferece possibilidades quase
ilimitadas para exibir esse tipo de dados tridimensionais em um gráfico bidimensional. Para mais
informações sobre
opções disponíveis nessas funções, consulte seus documentos. Se você estiver interessado em
visualizações tridimensionais deste tipo de dados, consulte “Plotagem tridimensional no Matplotlib”
na página 290.
Em [1]: % matplotlib
importação inline
numpy como np importação
matplotlib.pyplot como plt plt.style.use('seaborn-white')
dados = np.random.randn(1000)
Em[2]: plt.hist(dados);
A função hist() possui muitas opções para ajustar o cálculo e a exibição; aqui está um exemplo de
um histograma mais personalizado (Figura 4-36):
A documentação plt.hist contém mais informações sobre outras opções de personalização disponíveis.
Acho que esta combinação de histtype='stepfilled' junto com alguma transparência alfa é muito útil ao
comparar histogramas de diversas distribuições (Figura 4-37):
plt.hist(x1, **kwargs)
plt.hist(x2, **kwargs)
plt.hist(x3, **kwargs);
Se você quiser simplesmente calcular o histograma (ou seja, contar o número de pontos em um
determinado compartimento) e não exibi-lo, a função np.histogram() está disponível:
Assim como plt.hist, plt.hist2d tem uma série de opções extras para ajustar o gráfico e
o binning, que são bem descritos na documentação da função. Além disso, assim como
plt.hist tem uma contraparte em np.histogram, plt.hist2d tem uma contraparte em
np.histogram2d, que pode ser usada da seguinte forma:
plt.hexbin: binnings
hexagonais O histograma bidimensional cria um mosaico de quadrados ao longo dos eixos.
Outra forma natural para tal mosaico é o hexágono regular. Para esse propósito, Matplotlib
fornece a rotina plt.hexbin , que representa um conjunto de dados bidimensional agrupado em
uma grade de hexágonos (Figura 4-39):
plt.hexbin tem uma série de opções interessantes, incluindo a capacidade de especificar pesos
para cada ponto e alterar a saída em cada compartimento para qualquer agregado NumPy
(média de pesos, desvio padrão de pesos, etc.).
Estimativa de densidade
O KDE possui um comprimento de suavização que desliza efetivamente o botão entre detalhe e suavidade (um
exemplo da onipresente compensação entre polarização e variação). A literatura sobre a escolha de um
comprimento de suavização apropriado é vasta: gaussian_kde usa uma regra prática para tentar encontrar um
comprimento de suavização quase ideal para os dados de entrada.
Outras implementações do KDE estão disponíveis no ecossistema SciPy, cada uma com seus próprios pontos
fortes e fracos; veja, por exemplo, sklearn.neighbors.Kernel Density e
statsmodels.nonparametric.kernel_density.KDEMultivariate. Para visualizações baseadas no KDE, o uso do
Matplotlib tende a ser excessivamente detalhado. A biblioteca Seaborn, discutida em “Visualização com
Seaborn” na página 311, fornece uma API muito mais concisa para criar visualizações baseadas no KDE.
As legendas do enredo dão significado a uma visualização, atribuindo rótulos aos vários elementos do enredo.
Vimos anteriormente como criar uma legenda simples; aqui daremos uma olhada na personalização do
posicionamento e da estética da legenda no Matplotlib.
A legenda mais simples pode ser criada com o comando plt.legend() , que cria automaticamente uma legenda
para qualquer elemento rotulado do gráfico (Figura 4-41):
Mas há muitas maneiras pelas quais podemos querer personalizar essa lenda. Por exemplo, podemos especificar a
localização e desligar o quadro (Figura 4-42):
Podemos usar uma caixa arredondada (fancybox) ou adicionar uma sombra, alterar a transparência (valor alfa) do
quadro ou alterar o preenchimento ao redor do texto (Figura 4-44):
Para obter mais informações sobre as opções de legenda disponíveis, consulte a documentação plt.legend .
vimos, a legenda inclui todos os elementos rotulados por padrão. Se não for isso o desejado, podemos ajustar quais
elementos e rótulos aparecem na legenda usando os objetos retornados pelos comandos de plotagem. O comando
plt.plot() é capaz de criar várias linhas de uma vez e retorna uma lista de instâncias de linha criadas. Passar qualquer
um deles para plt.legend() dirá qual identificar, juntamente com os rótulos que gostaríamos de especificar (Figura
4.45):
Geralmente acho que na prática é mais claro usar o primeiro método, aplicando rótulos aos elementos do
gráfico que você gostaria de mostrar na legenda (Figura 4-46):
Observe que, por padrão, a legenda ignora todos os elementos sem um conjunto de atributos de rótulo .
Às vezes, os padrões da legenda não são suficientes para a visualização fornecida. Por exemplo, talvez
você esteja usando o tamanho dos pontos para marcar certos recursos dos dados e queira criar uma
legenda refletindo isso. Aqui está um exemplo em que usaremos o tamanho dos pontos para indicar as
populações das cidades da Califórnia. Gostaríamos de uma legenda que especificasse o
escala dos tamanhos dos pontos, e faremos isso traçando alguns dados rotulados sem entradas (Figura
4-47):
# Espalhe os pontos, usando tamanho e cor, mas sem rótulo plt.scatter(lon, lat,
label=None,
c=np.log10(população), cmap='viridis', s=area, largura de
linha=0, alfa=0,5) plt.axis(aspect='equal')
plt.xlabel('longitude') plt.ylabel('
latitude') plt.colorbar(label='log$_{10}$
(população)') plt.clim(3, 7)
A legenda sempre fará referência a algum objeto que esteja no gráfico, portanto, se quisermos exibir
uma forma específica, precisaremos plotá-la. Neste caso, os objetos que queremos (círculos cinzentos)
não estão no gráfico, então os falsificamos traçando listas vazias. Observe também que a legenda lista
apenas os elementos do gráfico que possuem um rótulo especificado.
Ao plotar listas vazias, criamos objetos de plotagem rotulados que são captados pela legenda, e agora nossa
legenda nos fornece algumas informações úteis. Esta estratégia pode ser útil para criar visualizações mais
sofisticadas.
Finalmente, observe que para dados geográficos como este, seria mais claro se pudéssemos mostrar os
limites dos estados ou outros elementos específicos do mapa. Para isso, uma excelente escolha de
ferramenta é o kit de ferramentas complementares Basemap do Matplotlib, que exploraremos em “Dados
geográficos com mapa base” na página 298.
Múltiplas Legendas Às
vezes, ao projetar um gráfico, você gostaria de adicionar múltiplas legendas aos mesmos eixos.
Infelizmente, o Matplotlib não facilita isso: através da interface de legenda padrão , só é possível criar uma
única legenda para todo o gráfico. Se você tentar criar uma segunda legenda usando plt.legend() ou
ax.legend(), ela simplesmente substituirá a primeira. Podemos contornar isso criando um novo artista de
legenda do zero e, em seguida, usando o método ax.add_artist() de nível inferior para adicionar manualmente
o segundo artista ao gráfico (Figura 4-48):
linhas = []
estilos = ['-', '--', '-.', ':'] x = np.linspace(0,
10, 1000)
para i no intervalo
(4): linhas += ax.plot(x, np.sin(x - i * np.pi / 2), estilos[i],
color='preto')
machado.axis('igual')
Esta é uma espiada nos objetos artísticos de baixo nível que compõem qualquer gráfico do Matplotlib. Se você
examinar o código fonte de ax.legend() (lembre-se de que você pode fazer isso dentro do notebook IPython usando
ax.legend??) você verá que a função consiste simplesmente em alguma lógica para criar um artista Legend adequado. ,
que é então salvo no atributo legend_ e adicionado à figura quando o gráfico é desenhado.
As legendas do gráfico identificam rótulos discretos de pontos discretos. Para rótulos contínuos baseados na cor de
pontos, linhas ou regiões, uma barra de cores rotulada pode ser uma ótima ferramenta. No Mat-plotlib, uma barra de
cores é um eixo separado que pode fornecer uma chave para o significado das cores em um gráfico. Como o livro é
impresso em preto e branco, esta seção tem um apêndice on-line onde você pode visualizar as figuras em cores
(https://github.com/jakevdp/PythonDataScienceHandbook). Começaremos configurando o notebook para plotar e
importar as funções que usaremos:
Como vimos diversas vezes ao longo desta seção, a barra de cores mais simples pode ser criada com a função
plt.colorbar (Figura 4-49):
plt.imshow(I)
plt.colorbar();
Discutiremos agora algumas idéias para personalizar essas barras de cores e usá-las de forma eficaz em diversas situações.
Podemos especificar o mapa de cores usando o argumento cmap para a função de plotagem que está criando a visualização
(Figura 4-50):
Todos os mapas de cores disponíveis estão no namespace plt.cm ; usar o recurso de preenchimento de guias do IPython
fornecerá uma lista completa de possibilidades integradas:
plt.cm.<TAB>
Mas poder escolher um mapa de cores é apenas o primeiro passo: mais importante é como decidir entre as possibilidades! A
escolha acaba sendo muito mais sutil do que se poderia esperar inicialmente.
Escolhendo o mapa de
cores Um tratamento completo da escolha de cores na visualização está além do escopo deste livro, mas
para uma leitura divertida sobre este assunto e outros, consulte o artigo “Dez regras simples para obter
melhores figuras”. A documentação online do Matplotlib também apresenta uma discussão interessante
sobre a escolha do mapa de cores.
Em termos gerais, você deve estar ciente de três categorias diferentes de mapas de cores:
Mapas de cores
sequenciais Consistem em uma sequência contínua de cores (por exemplo, binária ou viridis).
Mapas de cores
divergentes Geralmente contêm duas cores distintas, que mostram desvios positivos e negativos de
uma média (por exemplo, RdBu ou PuOr).
O mapa de cores jet , que era o padrão no Matplotlib antes da versão 2.0, é um exemplo de mapa de cores
qualitativo. O seu estatuto de padrão foi bastante lamentável, porque os mapas qualitativos são muitas vezes
uma má escolha para representar dados quantitativos.
Entre os problemas está o fato de que mapas qualitativos geralmente não exibem nenhuma progressão
uniforme no brilho à medida que a escala aumenta.
Podemos ver isso convertendo a barra de cores do jato em preto e branco (Figura 4-51):
In[5]:
de matplotlib.colors import LinearSegmentedColormap
def grayscale_cmap(cmap):
"""Retorna uma versão em escala de cinza do mapa de cores fornecido"""
cmap = plt.cm.get_cmap(cmap) cores
= cmap(np.arange(cmap.N))
def view_colormap(cmap):
"""Planeie um mapa de cores com seu equivalente em escala de
cinza""" cmap = plt.cm.get_cmap(cmap)
cores = cmap(np.arange(cmap.N))
Em[6]: view_colormap('jato')
Observe as listras brilhantes na imagem em tons de cinza. Mesmo em cores, esse brilho irregular significa
que o olhar será atraído para certas partes da gama de cores, o que potencialmente enfatizará partes sem
importância do conjunto de dados. É melhor usar um mapa de cores como o viridis (o padrão no Matplotlib
2.0), que é construído especificamente para ter uma variação uniforme de brilho em toda a faixa. Assim,
ele não apenas funciona bem com a nossa percepção de cores, mas também se traduz bem na impressão
em escala de cinza (Figura 4-52):
Em[7]: view_colormap('viridis')
Se você prefere esquemas arco-íris, outra boa opção para dados contínuos é o mapa de cores cubehelix
(Figura 4-53):
Em [8]: view_colormap('cubehelix')
Para outras situações, como mostrar desvios positivos e negativos de alguma média, barras de cores de
duas cores, como RdBu (abreviação de Vermelho-Azul), podem ser úteis. No entanto,
como você pode ver na Figura 4-54, é importante observar que as informações positivas-negativas serão
perdidas na tradução para escala de cinza!
Em[9]: view_colormap('RdBu')
Veremos exemplos do uso de alguns desses mapas de cores à medida que continuarmos.
Há um grande número de mapas de cores disponíveis no Matplotlib; para ver uma lista deles, você pode usar o
IPython para explorar o submódulo plt.cm. Para uma abordagem mais baseada em princípios para cores em
Python, você pode consultar as ferramentas e a documentação da biblioteca Seaborn (consulte “Visualização
com Seaborn” na página 311).
Matplotlib permite uma grande variedade de personalização da barra de cores. A barra de cores em si é
simplesmente uma instância de plt.Axes, portanto, todos os eixos e truques de formatação de ticks que
aprendemos são aplicáveis. A barra de cores tem uma flexibilidade interessante; por exemplo, podemos restringir
os limites de cores e indicar os valores fora dos limites com uma seta triangular na parte superior e inferior,
definindo a propriedade extend . Isso pode ser útil, por exemplo, se você estiver exibindo uma imagem sujeita a
ruído (Figura 4-55):
plt.figura(figsize=(10, 3,5))
plt.subplot(1, 2, 1)
plt.imshow(I, cmap='RdBu')
plt.colorbar()
plt.subplot(1, 2, 2)
plt.imshow(I, cmap='RdBu')
plt.colorbar(extend='ambos')
plt.clim(-1, 1);
Observe que no painel esquerdo, os limites de cores padrão respondem aos pixels ruidosos, e a faixa do ruído
elimina completamente o padrão no qual estamos interessados. No painel direito, definimos manualmente os
limites de cores e adicionamos extensões para indicar valores que estão acima ou abaixo desses limites. O
resultado é uma visualização muito mais útil de nossos dados.
Os mapas de cores são contínuos por padrão, mas às vezes você gostaria de representar valores discretos. A
maneira mais fácil de fazer isso é usar a função plt.cm.get_cmap() e passar o nome de um mapa de cores
adequado junto com o número de compartimentos desejados (Figura 4-56):
A versão discreta de um mapa de cores pode ser usada como qualquer outro mapa de cores.
Por enquanto, vamos começar baixando os dados dos dígitos e visualizando diversas imagens de
exemplo com plt.imshow() (Figura 4-57):
Como cada dígito é definido pela tonalidade dos seus 64 pixels, podemos considerar cada dígito como
um ponto situado num espaço de 64 dimensões: cada dimensão representa o brilho de um pixel. Mas
visualizar relacionamentos em espaços tão dimensionais pode ser extremamente difícil. Uma maneira
de abordar isso é usar uma técnica de redução de dimensionalidade, como o aprendizado múltiplo, para
reduzir a dimensionalidade dos dados enquanto mantém as relações de interesse. A redução da
dimensionalidade é um exemplo de aprendizado de máquina não supervisionado e discutiremos isso
com mais detalhes em “O que é aprendizado de máquina?” na página 332.
Adiando a discussão desses detalhes, vamos dar uma olhada em uma projeção bidimensional de
aprendizado múltiplo desses dados de dígitos (consulte “Detalhes: Aprendizado múltiplo” na página 445
para obter detalhes):
Usaremos nosso mapa de cores discreto para visualizar os resultados, definindo os ticks e clim
para melhorar a estética da barra de cores resultante (Figura 4-58):
A projeção também nos dá alguns insights interessantes sobre as relações dentro do conjunto de
dados: por exemplo, os intervalos de 5 e 3 quase se sobrepõem nesta projeção, indicando que
alguns cincos e três manuscritos são difíceis de distinguir e, portanto, mais propensos a serem
confundidos por um algoritmo de classificação automatizado. Outros valores, como 0 e 1, estão
separados mais distantemente e, portanto, têm muito menos probabilidade de serem confundidos.
Esta observação concorda com a nossa intuição, porque 5 e 3 parecem muito mais semelhantes
do que 0 e 1.
Várias subparcelas
Às vezes é útil comparar diferentes visualizações de dados lado a lado. Para tanto, o Matplotlib
possui o conceito de subtramas: grupos de eixos menores que podem existir juntos dentro de
uma única figura. Essas subtramas podem ser inserções, grades de plotagens ou outros layouts
mais complicados. Nesta seção, exploraremos quatro rotinas para criar subtramas no Matplotlib.
Começaremos configurando o notebook para plotagem e importação das funções que utilizaremos:
Em [1]: % matplotlib
importação inline matplotlib.pyplot
como plt plt.style.use('seaborn-
white') importação numpy como np
Por exemplo, podemos criar eixos inseridos no canto superior direito de outros eixos definindo
a posição xey como 0,65 (ou seja, começando em 65% da largura e 65% da altura da figura)
e o x e y se estendem até 0,2 (ou seja, o tamanho dos eixos é 20% da largura e 20% da altura
da figura). A Figura 4-59 mostra o resultado deste código:
x = np.linspace(0, 10)
ax1.plot(np.sin(x))
ax2.plot(np.cos(x));
Agora temos dois eixos (o superior sem marcadores) que estão apenas se tocando: a parte inferior do painel
superior (na posição 0,5) corresponde à parte superior do painel inferior (na posição 0,1 + 0,4).
linhas alinhadas de subparcelas são uma necessidade comum o suficiente para que o Matplotlib tenha várias
rotinas convenientes que as tornam fáceis de criar. O nível mais baixo deles é plt.subplot(), que cria uma única
subtrama dentro de uma grade. Como você pode ver, este comando leva três argumentos inteiros – o número de
linhas, o número de colunas e o índice do gráfico a ser criado neste esquema, que vai do canto superior esquerdo
ao canto inferior direito (Figura 4 -61):
O comando plt.subplots_adjust pode ser usado para ajustar o espaçamento entre essas plotagens. O
código a seguir (cujo resultado é mostrado na Figura 4-62) usa o comando orientado a objetos
equivalente, fig.add_subplot():
que acabamos de descrever pode se tornar bastante tediosa quando você cria uma grande grade de
subparcelas, especialmente se quiser ocultar os rótulos dos eixos x e y nos gráficos internos. Para
este propósito, plt.subplots() é a ferramenta mais fácil de usar (observe o s no final das subtramas).
Em vez de criar uma única subparcela, esta função cria uma grade completa de subparcelas em uma
única linha, retornando-as em um array NumPy. Os argumentos são o número de linhas e o número
de colunas, juntamente com as palavras-chave opcionais sharex e sharey, que permitem especificar
as relações entre os diferentes eixos.
Aqui criaremos uma grade 2×3 de subparcelas, onde todos os eixos na mesma linha compartilham a
escala do eixo y e todos os eixos na mesma coluna compartilham a escala do eixo x (Figura 4-63) :
In[7]: # eixos estão em uma matriz bidimensional, indexados por [row, col] for i in
range(2): for j in
range(3): ax[i,
j].text(0.5, 0.5 , str((i, j)), fontsize=18,
ha='centro')
Figo
grade regular para subparcelas que abrangem várias linhas e colunas, plt.GridSpec() é a melhor
ferramenta. O objeto plt.GridSpec() não cria um gráfico por
em si; é simplesmente uma interface conveniente reconhecida pelo comando plt.subplot() . Por exemplo, um
gridspec para uma grade de duas linhas e três colunas com algum espaço de largura e altura especificados
se parece com isto:
A partir disso, podemos especificar localizações e extensões de subtramas usando a familiar sintaxe de
fatiamento do Python (Figura 4-65):
Este tipo de alinhamento de grade flexível tem uma ampla gama de utilizações. Eu o uso com mais frequência
ao criar gráficos de histograma multieixos como o mostrado aqui (Figura 4-66):
Esse tipo de distribuição plotada ao longo de suas margens é comum o suficiente para ter sua
própria API de plotagem no pacote Seaborn; consulte “Visualização com Seaborn” na página
311 para obter mais detalhes.
Texto e anotação
Criar uma boa visualização envolve orientar o leitor para que a figura conte uma história. Em
alguns casos, esta história pode ser contada de forma totalmente visual, sem a necessidade
de adição de texto, mas em outros, são necessárias pequenas dicas e rótulos textuais. Talvez
os tipos mais básicos de anotações que você usará sejam rótulos e títulos de eixos, mas as
opções vão além disso. Vamos dar uma olhada em alguns dados e como podemos visualizá-
los e anotá-los para ajudar a transmitir informações interessantes. Começaremos configurando
o notebook para plotar e importar as funções que usaremos:
Em [1]: % matplotlib
importação inline matplotlib.pyplot
como plt importar matplotlib
como mpl plt.style.use('seaborn-whitegrid')
importar numpy
como np importar pandas como pd
EUA Voltemos a alguns dados com os quais trabalhamos anteriormente em “Exemplo: Dados da taxa de
natalidade” na página 174, onde geramos um gráfico da média de nascimentos ao longo do ano civil; como
já mencionado, esses dados podem ser baixados em https://raw.githubusercon tent.com/jakevdp/data-
CDCbirths/master/births.csv.
Em[2]:
nascimentos = pd.read_csv('nascimentos.csv')
nascimentos['dia'] = nascimentos['dia'].astype(int)
Quando comunicamos dados como estes, muitas vezes é útil anotar certas características do enredo para
chamar a atenção do leitor. Isso pode ser feito manualmente com o comando plt.text/ax.text , que colocará o
texto em um valor x/y específico (Figura 4-68):
# Rotule os eixos
ax.set(title=' Nascimentos nos EUA por dia do ano (1969-1988)',
ylabel='média de nascimentos diários')
O método ax.text assume uma posição x, uma posição y, uma string e, em seguida, palavras-chave
opcionais especificando a cor, tamanho, estilo, alinhamento e outras propriedades do texto.
Aqui usamos ha='right' e ha='center', onde ha é a abreviação de alinhamento horizontal. Consulte a
documentação de plt.text() e de mpl.text.Text() para obter mais informações sobre as opções disponíveis.
No exemplo anterior, ancoramos nossas anotações de texto em locais de dados. Às vezes é preferível
ancorar o texto em uma posição nos eixos ou na figura, independente dos dados. No Matplotlib,
fazemos isso modificando a transformação.
Qualquer estrutura de exibição gráfica precisa de algum esquema para tradução entre sistemas de
coordenadas. Por exemplo, um ponto de dados em x, y = 1, 1 precisa ser representado de alguma forma em
um determinado local da figura, que por sua vez precisa ser representado em pixels na tela. Matematicamente,
tais transformações de coordenadas são relativamente simples, e o Matplotlib possui um conjunto bem
desenvolvido de ferramentas que utiliza internamente para realizá-las (as ferramentas podem ser exploradas
no submódulo matplotlib.transforms ) .
O usuário médio raramente precisa se preocupar com os detalhes dessas transformações, mas é útil ter
conhecimento ao considerar o posicionamento do texto em uma figura. Existem três transformações
predefinidas que podem ser úteis nesta situação:
ax.transData
Transformação associada às coordenadas de dados
ax.transAxes
Transformada associada aos eixos (em unidades de dimensões dos eixos)
fig.transFigure
Transformação associada à figura (em unidades de dimensões da figura)
Vejamos aqui um exemplo de desenho de texto em vários locais usando essas transformações (Figura 4-69):
Observe que, por padrão, o texto está alinhado acima e à esquerda das coordenadas especificadas; aqui o
"." no início de cada string marcará aproximadamente a localização da coordenada fornecida.
As coordenadas transData fornecem as coordenadas de dados usuais associadas aos rótulos dos eixos x e
y. As coordenadas transAxes fornecem a localização do canto inferior esquerdo dos eixos (aqui a caixa
branca) como uma fração do tamanho dos eixos. As coordenadas da transFigura são semelhantes, mas
especificam a posição na parte inferior esquerda da figura (aqui a caixa cinza) como uma fração do tamanho
da figura.
Observe agora que se alterarmos os limites dos eixos, apenas as coordenadas transData serão afetadas,
enquanto as demais permanecerão estacionárias (Figura 4-70):
Em [6]: ax.set_xlim(0, 2)
ax.set_ylim(-6, 6) fig.
Você pode ver esse comportamento mais claramente alterando os limites dos eixos de forma interativa; se
você estiver executando esse código em um notebook, poderá fazer isso alterando %mat plotlib inline para
%matplotlib notebook e usando o menu de cada gráfico para interagir com o gráfico.
Setas e anotação
Junto com as marcas de escala e o texto, outra marca de anotação útil é a seta simples.
Desenhar setas no Matplotlib costuma ser muito mais difícil do que você imagina. Embora exista uma função
plt.arrow() disponível, eu não sugeriria usá-la; as setas que ele cria são objetos SVG que estarão sujeitos às
proporções variáveis de seus gráficos, e o resultado raramente é o que o usuário pretendia. Em vez disso,
sugiro usar a função plt.anno tate() . Esta função cria algum texto e uma seta, e as setas podem ser
especificadas de forma muito flexível.
O estilo da seta é controlado através do dicionário arrowprops , que possui inúmeras opções
disponíveis. Essas opções estão bastante bem documentadas na documentação on-line do
Matplotlib, então, em vez de repeti-las aqui, mostrarei rapidamente algumas das
possibilidades. Vamos demonstrar várias das opções possíveis usando o gráfico anterior da
taxa de natalidade (Figura 4-72):
Em
[8]: fig, ax = plt.subplots(figsize=(12, 4))
nascimentos_por_data.plot(ax=ax)
# Rotule os eixos
ax.set(title=' Nascimentos nos EUA por dia do ano (1969-1988)',
ylabel='média de nascimentos diários')
ax.set_ylim(3600, 5400);
Você notará que as especificações das setas e caixas de texto são muito detalhadas: isso lhe dá o
poder de criar praticamente qualquer estilo de seta que desejar. Infelizmente, isso também significa
que esses tipos de recursos muitas vezes devem ser ajustados manualmente, um processo que pode
consumir muito tempo quando se produz gráficos com qualidade de publicação! Por fim, observarei que
a combinação de estilos anterior não é de forma alguma uma prática recomendada para apresentação
de dados, mas sim incluída como uma demonstração de algumas das opções disponíveis.
Mais discussões e exemplos de estilos de setas e anotações disponíveis podem ser encontrados na
galeria Matplotlib, em particular http://matplotlib.org/examples/pylab_examples/annotation_demo2.html.
Personalizando ticks
Os localizadores e formatadores de ticks padrão do Matplotlib são projetados para serem geralmente
suficientes em muitas situações comuns, mas não são de forma alguma ideais para todos os gráficos.
Esta seção dará vários exemplos de ajuste dos locais dos ticks e formatação para o tipo de gráfico
específico no qual você está interessado.
Antes de entrarmos nos exemplos, será melhor entendermos melhor a hierarquia de objetos dos
gráficos do Matplotlib. Matplotlib pretende ter um objeto Python representando tudo o que aparece no
gráfico: por exemplo, lembre-se que a figura é a caixa delimitadora dentro da qual os elementos do
gráfico aparecem. Cada objeto Matplotlib também pode atuar como um contêiner de subobjetos; por
exemplo, cada figura pode conter um ou mais objetos de eixo , cada um dos quais, por sua vez, contém
outros objetos que representam o conteúdo do gráfico.
As marcas de escala não são exceção. Cada eixo possui atributos xaxis e yaxis, que por sua vez
possuem atributos que contêm todas as propriedades das linhas, marcas e rótulos que compõem os
eixos.
Marcações principais e
secundárias Dentro de cada eixo, existe o conceito de uma marca de escala principal e de uma marca
de escala secundária. Como os nomes indicam, os ticks principais são geralmente maiores ou mais
pronunciados, enquanto os ticks menores são geralmente menores. Por padrão, o Matplotlib raramente
faz uso de ticks menores, mas um lugar onde você pode vê-los é nos gráficos logarítmicos (Figura 4-73):
Em [1]: % matplotlib
importação inline matplotlib.pyplot
como plt plt.style.use('seaborn-whitegrid')
importação numpy como np
Vemos aqui que cada marca principal mostra uma marca grande e um rótulo, enquanto cada marca
secundária mostra uma marca menor sem rótulo.
Podemos personalizar essas propriedades de tick – ou seja, locais e rótulos – definindo o formatador e
os objetos localizadores de cada eixo. Vamos examiná-los para o eixo x do gráfico que acabamos de
mostrar:
Em[3]: imprimir(ax.xaxis.get_major_locator())
imprimir(ax.xaxis.get_minor_locator())
Em[4]: imprimir(ax.xaxis.get_major_formatter())
imprimir(ax.xaxis.get_minor_formatter())
Vemos que os rótulos de escala principais e secundários têm suas localizações especificadas por um
LogLocator (o que faz sentido para um gráfico logarítmico). Os ticks menores, porém, têm seus rótulos
formatados por um NullFormatter; isso indica que nenhum rótulo será mostrado.
Mostraremos agora alguns exemplos de configuração desses localizadores e formatadores para vários
gráficos.
Talvez a operação de formatação de marca/rótulo mais comum seja o ato de ocultar marcações ou
rótulos. Podemos fazer isso usando plt.NullLocator() e plt.NullFormatter(), conforme mostrado aqui
(Figura 4-74):
ax.yaxis.set_major_locator(plt.NullLocator())
ax.xaxis.set_major_formatter(plt.NullFormatter())
Figura 4-74. Gráfico com rótulos de marca ocultos (eixo x) e marcações ocultas (eixo y)
Observe que removemos os rótulos (mas mantivemos as marcas/linhas de grade) do eixo x e removemos
as marcações (e, portanto, os rótulos também) do eixo y. Não ter nenhuma marca pode ser útil em muitas
situações — por exemplo, quando você deseja mostrar uma grade de imagens. Por exemplo, considere
a Figura 4-75, que inclui imagens de diferentes faces, um exemplo frequentemente usado em problemas
de aprendizado de máquina supervisionado (para obter mais informações, consulte “Detalhamento:
Máquinas de vetores de suporte” na página 405):
Observe que cada imagem tem seus próprios eixos e definimos os localizadores como nulos porque
os valores dos ticks (neste caso, o número do pixel) não transmitem informações relevantes para
esta visualização específica.
problema comum com as configurações padrão é que subparcelas menores podem acabar com
rótulos lotados. Podemos ver isso na grade mostrada na Figura 4-76:
Isso torna as coisas muito mais limpas. Se quiser ainda mais controle sobre a localização dos
ticks regularmente espaçados, você também pode usar plt.MultipleLocator, que discutiremos
na seção seguinte.
Formatos de tick
sofisticados A formatação de tick padrão do Matplotlib pode deixar muito a desejar; funciona
bem como um padrão amplo, mas às vezes você gostaria de fazer algo mais. Considere o
gráfico mostrado na Figura 4-78, um seno e um cosseno:
Há algumas mudanças que gostaríamos de fazer. Primeiro, é mais natural que esses dados espacem os
ticks e as linhas de grade em múltiplos de ÿ. Podemos fazer isso definindo um Multi locator, que localiza
os ticks em um múltiplo do número que você forneceu. Para garantir, adicionaremos ticks maiores e
menores em múltiplos de ÿ/4 (Figura 4-79):
Mas agora esses rótulos de escala parecem um pouco bobos: podemos ver que são múltiplos de ÿ, mas
a representação decimal não transmite isso imediatamente. Para corrigir isso, podemos alterar o
formatador do tick. Não há um formatador integrado para o que queremos fazer, então usaremos
plt.FuncFormatter, que aceita uma função definida pelo usuário, fornecendo controle refinado sobre as
saídas de ticks (Figura 4-80):
elif N == 1:
retornar r"$\pi/2$" elif N
== 2:
retornar r"$\pi$" elif
N % 2 > 0: retornar
r"${0}\pi/2$".format(N) senão: retornar
r"${0}\pi$".format(N) // 2)
ax.xaxis.set_major_formatter(plt.FuncFormatter(format_func)) fig.
Isto é muito melhor! Observe que usamos o suporte LaTeX do Matplotlib, especificado
colocando a string entre cifrões. Isto é muito conveniente para exibição de símbolos e
fórmulas matemáticas; neste caso, "$\pi$" é traduzido como o caractere grego ÿ.
Localizador de índice Localizador para gráficos de índice (por exemplo, onde x = range(len(y)))
Aqui, examinaremos algumas das opções de configuração de tempo de execução (rc) do Matplotlib e
dê uma olhada no recurso de folhas de estilo mais recente, que contém alguns bons conjuntos de padrões
configurações.
Ao longo deste capítulo, vimos como é possível ajustar o conjunto individual do enredo.
coisas para acabar com algo que parece um pouco melhor que o padrão. É possível
capaz de fazer essas personalizações para cada parcela individual. Por exemplo, aqui está um bastante
Histograma padrão monótono (Figura 4-81):
% matplotlib embutido
Em[2]: x = np.random.randn(1000)
plt.hist(x);
Podemos ajustar isso manualmente para torná-lo um gráfico muito mais agradável visualmente, mostrado em
Figura 4-82:
# ocultar as espinhas
do eixo para a espinha em
ax.spines.values(): espinha.set_visible(False)
Isso parece melhor, e você pode reconhecer a aparência inspirada na aparência do pacote de visualização
ggplot da linguagem R. Mas isso exigiu muito esforço! Definitivamente não queremos ter que fazer todos
aqueles ajustes cada vez que criamos um enredo. Felizmente, existe uma maneira de ajustar esses
padrões uma vez, de uma forma que funcione para todos os gráficos.
o Matplotlib é carregado, ele define uma configuração de tempo de execução (rc) contendo os estilos
padrão para cada elemento do gráfico que você cria. Você pode ajustar esta configuração a qualquer
momento usando a rotina de conveniência plt.rc. Vamos ver como é modificar os parâmetros rc para que
nosso gráfico padrão fique semelhante ao que fizemos antes.
Começaremos salvando uma cópia do dicionário rcParams atual , para que possamos redefinir facilmente
essas alterações na sessão atual:
Agora podemos usar a função plt.rc para alterar algumas destas configurações:
Com essas configurações definidas, agora podemos criar um gráfico e ver nossas configurações em ação
(Figura 4-83):
Em[6]: plt.hist(x);
Vamos ver como são os gráficos de linhas simples com esses parâmetros rc (Figura 4-84):
Acho isso muito mais esteticamente agradável do que o estilo padrão. Se você discorda do meu senso estético, a
boa notícia é que você pode ajustar os parâmetros do rc de acordo com seu gosto! Essas configurações podem ser
salvas em um arquivo .matplotlibrc, sobre o qual você pode ler na documentação do Matplotlib. Dito isto, prefiro
personalizar o Mat-plotlib usando suas folhas de estilo.
Folhas de estilo
A versão 1.4 do Matplotlib em agosto de 2014 adicionou um módulo de estilo muito conveniente , que inclui uma
série de novas folhas de estilo padrão, bem como a capacidade de criar e empacotar seus próprios estilos. Essas
folhas de estilo são formatadas de forma semelhante aos arquivos .matplotlibrc mencionados anteriormente, mas
devem ser nomeadas com uma extensão .mplstyle.
Mesmo que você não crie seu próprio estilo, as folhas de estilo incluídas por padrão são extremamente úteis. Os
estilos disponíveis estão listados em plt.style.available - aqui listarei apenas os cinco primeiros por questões de
brevidade:
Em[8]: plt.style.available[:5]
plt.style.use('nome do estilo')
Mas lembre-se de que isso mudará o estilo do resto da sessão! Alternativamente, você pode usar o gerenciador
de contexto de estilo, que define um estilo temporariamente:
com plt.style.context('stylename'):
make_a_plot()
Vamos criar uma função que fará dois tipos básicos de gráfico:
Usaremos isso para explorar a aparência desses gráficos usando os vários estilos integrados.
Estilo padrão
O estilo padrão é o que vimos até agora ao longo do livro; vamos começar com isso. Primeiro, vamos redefinir
nossa configuração de tempo de execução para o padrão do notebook:
Em[11]: hist_and_lines()
Estilo
FiveThirtyEight O estilo FiveThirtyEight imita os gráficos encontrados no popular site
FiveThirtyEight. Como você pode ver na Figura 4-86, ela é caracterizada por cores fortes,
linhas grossas e eixos transparentes.
ggplot
para Hackers Há um pequeno livro online muito bom chamado Programação Probabilística e
Métodos Bayesianos para Hackers; ele apresenta figuras criadas com Matplotlib e usa um bom
conjunto de parâmetros rc para criar um estilo consistente e visualmente atraente ao longo do livro.
Este estilo é reproduzido na folha de estilo bmh (Figura 4-88):
Fundo escuro
Para figuras usadas em apresentações, geralmente é útil ter um fundo escuro em vez de claro.
O estilo dark_background fornece isto (Figura 4-89):
Escala de
cinza Às vezes, você pode estar preparando figuras para uma publicação impressa que não aceita figuras
coloridas. Para isso, o estilo tons de cinza , mostrado na Figura 4-90, pode ser muito útil:
Estilo Seaborn
O Matplotlib também possui folhas de estilo inspiradas na biblioteca Seaborn (discutida mais detalhadamente
em “Visualização com Seaborn” na página 311). Como veremos, esses estilos são carregados
automaticamente quando o Seaborn é importado para um notebook. Achei essas configurações muito boas
e costumo usá-las como padrão em minha própria exploração de dados (veja a Figura 4-91):
In[17]: importar
hist_and_lines() do mar
Com todas essas opções integradas para vários estilos de plotagem, o Matplotlib se torna muito mais útil
tanto para visualização interativa quanto para criação de figuras para publicação.
Ao longo deste livro, geralmente usarei uma ou mais dessas convenções de estilo ao criar gráficos.
Uma vez importado este submódulo, podemos criar eixos tridimensionais passando a palavra-chave
project='3d' para qualquer uma das rotinas normais de criação de eixos:
Com esses eixos 3D ativados, agora podemos traçar uma variedade de tipos de gráficos tridimensionais.
A plotagem tridimensional é uma das funcionalidades que se beneficia imensamente da visualização de
figuras de forma interativa, em vez de estática, no notebook; lembre-se de que para usar figuras
interativas, você pode usar o notebook %matplotlib em vez de %matplotlib inline ao executar este código.
Observe que, por padrão, os pontos de dispersão têm sua transparência ajustada para dar
uma sensação de profundidade à página. Embora o efeito tridimensional às vezes seja difícil
de ver em uma imagem estática, uma visualização interativa pode levar a uma boa intuição
sobre o layout dos pontos.
x = np.linspace(-6, 6, 30) y =
np.linspace(-6, 6, 30)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
Às vezes, o ângulo de visão padrão não é o ideal; nesse caso, podemos usar o método view_init
para definir os ângulos de elevação e azimutais. Neste exemplo (cujo resultado é mostrado na
Figura 4-95), usaremos uma elevação de 60 graus (ou seja, 60 graus acima do plano xy) e um
azimute de 35 graus (ou seja, girado 35 graus sentido anti-horário em torno do eixo z):
Novamente, observe que podemos realizar esse tipo de rotação interativamente clicando e
arrastando ao usar um dos backends interativos do Matplotlib.
Dois outros tipos de gráficos tridimensionais que funcionam em dados de grade são wireframes
e gráficos de superfície. Eles pegam uma grade de valores e a projetam na superfície
tridimensional especificada, e podem tornar as formas tridimensionais resultantes bastante fáceis
de visualizar. Aqui está um exemplo usando um wireframe (Figura 4-96):
Um gráfico de superfície é como um gráfico de modelo de arame, mas cada face do modelo de arame é um
polígono preenchido. Adicionar um mapa de cores aos polígonos preenchidos pode ajudar na percepção da
topologia da superfície que está sendo visualizada (Figura 4-97):
Em [9]: ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap='viridis', edgecolor='none')
ax.set_title('surface');
Observe que embora a grade de valores para um gráfico de superfície precise ser bidimensional, ela não precisa
ser retilínea. Aqui está um exemplo de criação de uma grade polar parcial, que quando usada com o gráfico de
superfície3D pode nos fornecer uma fatia da função que estamos visualizando (Figura 4-98):
X=r * np.sin(teta) *
S=r np.cos(teta)
Z = f(X, Y)
machado = plt.axes(projeção='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap='viridis', edgecolor='none');
algumas aplicações, as grades amostradas uniformemente exigidas pelas rotinas anteriores são excessivamente
restritivas e inconvenientes. Nessas situações, os gráficos baseados em triangulação podem ser muito úteis. E se,
em vez de um sorteio par de uma grade cartesiana ou polar, tivéssemos um conjunto de sorteios aleatórios?
Poderíamos criar um gráfico de dispersão dos pontos para ter uma ideia da superfície da qual estamos amostrando
(Figura 4-99):
Isso deixa muito a desejar. A função que nos ajudará neste caso é ax.plot_trisurf, que cria uma superfície
encontrando primeiro um conjunto de triângulos formados entre pontos adjacentes (o resultado é
mostrado na Figura 4-100; lembre-se de que x, y e z aqui são matrizes unidimensionais):
Em[13]: ax = plt.axes(projection='3d')
ax.plot_trisurf(x, y, z,
cmap='viridis', edgecolor='none');
O resultado certamente não é tão limpo como quando plotado com uma grade, mas a flexibilidade de tal
triangulação permite alguns gráficos tridimensionais realmente interessantes. Por exemplo, é realmente
possível traçar uma faixa de Möbius tridimensional usando isto, como veremos a seguir.
Möbius Uma tira de Möbius é semelhante a uma tira de papel colada em um laço com meia torção.
Topologicamente, é bastante interessante porque, apesar das aparências, tem apenas um lado!
Aqui visualizaremos tal objeto usando as ferramentas tridimensionais do Matplotlib. A chave para criar a
tira de Möbius é pensar na sua parametrização: é uma fita de dois
faixa dimensional, então precisamos de duas dimensões intrínsecas. Vamos chamá-los de ÿ, que
varia de 0 a 2ÿ ao redor do loop, e w que varia de –1 a 1 em toda a largura da tira:
Agora a partir desta parametrização devemos determinar as posições (x, y, z) da faixa embutida.
Pensando nisso, podemos perceber que há duas rotações acontecendo: uma é a posição do laço
em torno de seu centro (o que chamamos de ÿ), enquanto a outra é a torção da tira em torno de
seu eixo (que chamamos de ÿ). vou chamar isso de ÿ). Para uma tira de Möbius, devemos fazer
com que a tira dê meia volta durante um loop completo, ou ÿÿ = ÿÿ/2.
x = np.ravel(r * np.cos(theta)) y =
np.ravel(r * np.sin(theta)) z = np.ravel(w
* np.sin(phi))
Finalmente, para plotar o objeto, devemos ter certeza de que a triangulação está correta. A melhor
maneira de fazer isso é definir a triangulação dentro da parametrização subjacente e, em seguida,
deixar o Matplotlib projetar essa triangulação no espaço tridimensional da faixa de Möbius. Isso
pode ser feito da seguinte maneira (Figura 4-101):
machado = plt.axes(projeção='3d')
ax.plot_trisurf(x, y, z, triângulos=tri.triângulos, cmap='viridis',
larguras de linha=0,2);
Combinando todas essas técnicas, é possível criar e exibir uma grande variedade de objetos e padrões
tridimensionais no Matplotlib.
A instalação do Basemap é simples; se estiver usando conda, você pode digitar isto e o pacote será
baixado:
Depois de instalar e importar o kit de ferramentas do Mapa Base, os gráficos geográficos estarão a
apenas algumas linhas de distância (os gráficos na Figura 4-102 também requerem o pacote PIL no
Python 2 ou o pacote pillow no Python 3):
O útil é que o globo aqui mostrado não é uma mera imagem; é um eixo Matplotlib
totalmente funcional que entende coordenadas esféricas e nos permite plotar facilmente
os dados no mapa! Por exemplo, podemos usar uma projeção de mapa diferente, ampliar
a América do Norte e traçar a localização de Seattle. Usaremos uma imagem etopo (que
mostra características topográficas tanto em terra quanto no fundo do oceano) como plano
de fundo do mapa (Figura 4-103):
Em [3]: fig = plt.figure(figsize=(8, 8)) m = Mapa
base(projeção='lcc', resolução=Nenhum, largura=8E6,
altura=8E6, lat_0=45,
lon_0=-100, )
m.etopo(escala=0,5, alfa=0,5)
Isso lhe dá uma breve visão do tipo de visualizações geográficas que são possíveis com apenas
algumas linhas de Python. Discutiremos agora os recursos do Mapa Base com mais detalhes e
forneceremos vários exemplos de visualização de dados de mapa. Usando esses breves exemplos
como blocos de construção, você poderá criar praticamente qualquer visualização de mapa que
desejar.
Projeções de mapas
A primeira coisa a decidir ao usar mapas é qual projeção usar. Você provavelmente está
familiarizado com o fato de que é impossível projetar um mapa esférico, como o da Terra, em uma
superfície plana sem de alguma forma distorcê-lo ou quebrar sua continuidade. Estas projeções
foram desenvolvidas ao longo da história humana e há muitas opções! Dependendo do uso
pretendido da projeção do mapa, há certas características do mapa (por exemplo, direção, área,
distância, formato ou outras considerações) que são úteis para manter.
O pacote Basemap implementa várias dezenas dessas projeções, todas referenciadas por um
código de formato curto. Aqui demonstraremos brevemente alguns dos mais comuns.
Começaremos definindo uma rotina de conveniência para desenhar nosso mapa mundial junto com
as linhas de longitude e latitude:
Projeções cilíndricas
As projeções cartográficas mais simples são as projeções cilíndricas, nas quais linhas de
latitude e longitude constantes são mapeadas em linhas horizontais e verticais,
respectivamente. Este tipo de mapeamento representa muito bem as regiões equatoriais,
mas resulta em distorções extremas perto dos pólos. O espaçamento das linhas de latitude
varia entre diferentes projeções cilíndricas, levando a diferentes propriedades de conservação
e diferentes distorções perto dos pólos. Na Figura 4-104, mostramos um exemplo de projeção
cilíndrica equidistante, que escolhe uma escala de latitude que preserva as distâncias ao longo dos meridianos.
Outras projeções cilíndricas são as projeções de Mercator (projeção='merc') e as projeções
cilíndricas de área igual (projeção='cea') .
Os argumentos adicionais do Mapa Base para esta visualização especificam a latitude (lat) e
a longitude (lon) do canto inferior esquerdo (llcrnr) e do canto superior direito (urcrnr) do
mapa desejado, em unidades de graus.
Projeções pseudocilíndricas
As projeções pseudocilíndricas relaxam a exigência de que os meridianos (linhas de longitude
constante) permaneçam verticais; isto pode dar melhores propriedades perto dos pólos da
projeção. A projeção de Mollweide (projeção='moll') é um exemplo comum disso, em que
todos os meridianos são arcos elípticos (Figura 4-105). Ele é construído de forma a preservar
a área do mapa: embora haja distorções perto dos pólos, a área de pequenas manchas reflete
a área real. Outras projeções pseudocilíndricas são as projeções sinusoidal (projeção='sinu')
e Robinson (projeção='robin') .
desenhar_mapa(m)
Os argumentos extras para o Mapa Base aqui referem-se à latitude central (lat_0) e à
longitude (lon_0) do mapa desejado.
Projeções em
Projeções
cônicas Uma projeção cônica projeta o mapa em um único cone, que é então
desenrolado. Isto pode levar a propriedades locais muito boas, mas as regiões distantes
do ponto de foco do cone podem ficar muito distorcidas. Um exemplo disso é a projeção
cônica conforme de Lambert (projeção='lcc'), que vimos anteriormente no mapa da América do Norte.
Ele projeta o mapa em um cone organizado de tal forma que dois paralelos padrão
(especificados no Mapa Base por lat_1 e lat_2) tenham distâncias bem representadas,
com escala diminuindo entre eles e aumentando fora deles. Outras projeções cônicas
úteis são a projeção cônica equidistante (projeção='eqdc') e a projeção de área igual de
Albers (projeção='aea') (Figura 4-107). As projeções cônicas, assim como as projeções
em perspectiva, tendem a ser boas escolhas para representar partes pequenas e
médias do globo.
Em [8]: fig = plt.figure(figsize=(8, 8)) m = Mapa
base(projeção='lcc', resolução=Nenhum, lon_0=0,
lat_0=50, lat_1=45, lat_2=55,
largura=1,6E7, altura=1,2E7)
desenhar_mapa(m)
Outras projeções
Se você pretende fazer muito com visualizações baseadas em mapas, recomendo que leia sobre outras
projeções disponíveis, juntamente com suas propriedades, vantagens e desvantagens. Muito provavelmente,
eles estão disponíveis no pacote Basemap. Se você se aprofundar o suficiente neste tópico, encontrará uma
subcultura incrível de geeks de geo-viz que estarão prontos para argumentar fervorosamente em apoio à sua
projeção favorita para qualquer aplicação!
Anteriormente vimos os métodos bluemarble() e shadedrelief() para projetar imagens globais no mapa, bem
como os métodos drawparallels() e drawmeridians() para desenhar linhas de latitude e longitude constantes.
O pacote Basemap contém uma série de funções úteis para desenhar fronteiras de características físicas
como continentes, oceanos, lagos e rios, bem como fronteiras políticas, como países e estados e condados
dos EUA. A seguir estão algumas das funções de desenho disponíveis que você pode explorar usando os
recursos de ajuda do IPython:
linhas costeiras()
Desenhe linhas da costa continental
máscara arrastada()
Desenhe uma máscara entre a terra e o mar, para usar na projeção de imagens em um ou outro
limite do mapa()
Desenhe os limites do mapa, incluindo a cor de preenchimento dos oceanos
drawrivers()
Desenhe rios no mapa
preenchercontinentes()
Preencha os continentes com uma determinada cor; opcionalmente, preencha os lagos com outra cor
• Limites políticos
desenharpaíses()
Desenhe limites do país
estados de empate()
Desenhe as fronteiras dos estados dos EUA
sorteio de condados()
Desenhe os limites dos condados dos EUA
• Recursos do mapa
desenhar paralelos()
Desenhe linhas de latitude constante
drawmeridianos()
Desenhe linhas de longitude constante
drawmapscale()
Desenhe uma escala linear no mapa
mármore azul()
Projete a imagem de mármore azul da NASA no mapa
relevo sombreado()
Projete uma imagem de relevo sombreado no mapa
etopo()
Desenhe uma imagem de relevo etopo no mapa
imagem warp()
Projete uma imagem fornecida pelo usuário no mapa
Para os recursos baseados em limites, você deve definir a resolução desejada ao criar uma
imagem de Mapa Base. O argumento de resolução da classe Basemap define o nível de detalhe
nos limites, seja 'c' (bruto), 'l' (baixo), 'i' (intermediário), 'h' (alto), 'f' (completo) ou None se nenhum
limite for usado. Esta escolha é importante: definir limites de alta resolução num mapa global,
por exemplo, pode ser muito lento.
Observe que os litorais de baixa resolução não são adequados para este nível de zoom, enquanto
a alta resolução funciona perfeitamente. No entanto, o nível baixo funcionaria muito bem para
uma visão global e seria muito mais rápido do que carregar os dados de fronteira de alta resolução
para todo o globo! Pode ser necessária alguma experimentação para encontrar a resolução correta
parâmetro para uma determinada visualização; o melhor caminho é começar com um gráfico rápido e de baixa resolução e
aumentar a resolução conforme necessário.
parte mais útil do kit de ferramentas do Mapa Base seja a capacidade de plotar uma variedade de dados no plano de fundo de
um mapa. Para plotagem e texto simples, qualquer função plt funciona no mapa; você pode usar a instância Basemap para
projetar coordenadas de latitude e longitude em coordenadas (x, y) para plotar com plt, como vimos anteriormente no exemplo
de Seattle.
Além disso, existem muitas funções específicas do mapa disponíveis como métodos da instância do Mapa Base . Eles
funcionam de forma muito semelhante às suas contrapartes padrão do Matplotlib, mas têm um argumento booleano adicional
latlon, que se definido como True permite que você passe latitudes e longitudes brutas para o método, em vez de coordenadas
projetadas (x, y) .
contorno()/contorno()
Desenhe linhas de contorno ou contornos preenchidos
mostrar()
Desenhe uma imagem
pcolor()/pcolormesh()
Desenhe um gráfico de pseudocor para malhas irregulares/regulares
trama()
Desenhe linhas e/ou marcadores
dispersão()
Desenhe pontos com marcadores
tremor()
Desenhar vetores
farpas()
Desenhe farpas de vento
Veremos exemplos de alguns deles à medida que continuarmos. Para obter mais informações sobre essas funções, incluindo
Lembre-se de que em “Personalizando legendas de lote” na página 249, demonstramos o uso de tamanho e cor
em um gráfico de dispersão para transmitir informações sobre a localização, o tamanho e a população das
cidades da Califórnia. Aqui, criaremos esse gráfico novamente, mas usando o Mapa Base para contextualizar os
dados.
A seguir, configuramos a projeção do mapa, dispersamos os dados e, em seguida, criamos uma barra de cores
e uma legenda (Figura 4-109):
m.drawcoastlines(color='gray')
m.drawcountries(color='gray')
m.drawstates(color='gray')
Isso nos mostra aproximadamente onde as maiores populações de pessoas se estabeleceram na Califórnia:
elas estão agrupadas perto da costa nas áreas de Los Angeles e São Francisco, espalhadas ao longo das
rodovias no vale central plano e evitando quase completamente as regiões montanhosas ao longo das
fronteiras de o Estado.
exemplo de visualização de alguns dados geográficos mais contínuos, vamos considerar o “vórtice polar” que
atingiu a metade oriental dos Estados Unidos em janeiro de 2014. Uma ótima fonte para qualquer tipo de
dados climáticos é o Instituto Goddard de Pesquisa da NASA . Estudos Espaciais. Aqui usaremos os dados
de temperatura do GIS 250, que podemos baixar usando comandos shell (esses comandos podem precisar
ser modificados em máquinas Windows). Os dados usados aqui foram baixados em 12/06/2016 e o tamanho
do arquivo é de aproximadamente 9
MB:
Os dados vêm no formato NetCDF, que pode ser lido em Python pela biblioteca netCDF4 . Você pode instalar
esta biblioteca conforme mostrado aqui:
O arquivo contém muitas leituras de temperatura global em diversas datas; precisamos selecionar o
índice da data em que estamos interessados – neste caso, 15 de janeiro de 2014:
Agora podemos carregar os dados de latitude e longitude, bem como a anomalia de temperatura
para este índice:
Finalmente, usaremos o método pcolormesh() para desenhar uma malha de cores dos dados.
Veremos a América do Norte e usaremos um mapa de relevo sombreado no fundo. Observe que
para esses dados escolhemos especificamente um mapa de cores divergente, que possui uma cor
neutra em zero e duas cores contrastantes em valores negativos e positivos (Figura 4-110). Também
desenharemos levemente as linhas costeiras sobre as cores para referência:
Os dados mostram um quadro das anomalias localizadas e extremas de temperatura que aconteceram
durante aquele mês. A metade oriental dos Estados Unidos estava muito mais fria do que o normal,
enquanto a metade ocidental e o Alasca estavam muito mais quentes. Regiões sem temperatura
registrada mostram o fundo do mapa.
• Antes da versão 2.0, os padrões do Matplotlib não eram exatamente as melhores escolhas. Foi
baseado no MATLAB por volta de 1999, e isso costuma ser mostrado.
• Matplotlib é anterior ao Pandas em mais de uma década e, portanto, não foi projetado para uso com
Pandas DataFrames. Para visualizar os dados de um DataFrame do Pandas, você deve extrair
cada série e frequentemente concatená-las no formato correto. Seria melhor ter uma biblioteca de
plotagem que pudesse usar de forma inteligente os rótulos do DataFrame em uma plotagem.
Uma resposta para esses problemas é Seaborn. Seaborn fornece uma API sobre Matplotlib que oferece
opções sensatas para estilo de plotagem e padrões de cores, define funções simples de alto nível para
tipos de plotagens estatísticas comuns e integra-se com a funcionalidade fornecida pelo Pandas
DataFrames .
Para ser justo, a equipe do Matplotlib está abordando isso: recentemente adicionou as ferramentas
plt.style (discutidas em “Personalizando o Matplotlib: configurações e folhas de estilo” na página
282) e está começando a lidar com os dados do Pandas de maneira mais integrada. A versão 2.0
da biblioteca incluirá uma nova folha de estilo padrão que melhorará o status quo atual. Mas, por
todas as razões discutidas, o Seaborn continua sendo um complemento extremamente útil.
está um exemplo de um gráfico de passeio aleatório simples no Matplotlib, usando sua formatação
e cores clássicas de gráfico. Começamos com as importações típicas:
Embora o resultado contenha todas as informações que gostaríamos que transmitisse, ele o faz de
uma forma que não é tão esteticamente agradável e até parece um pouco antiquado no contexto
da visualização de dados do século XXI.
Agora vamos dar uma olhada em como funciona com o Seaborn. Como veremos, Seaborn tem muitas de suas próprias
rotinas de plotagem de alto nível, mas também pode sobrescrever os parâmetros padrão do Matplotlib e, por sua vez,
fazer com que até mesmo scripts simples do Matplotlib produzam resultados muito superiores. Podemos definir o estilo
chamando o método set() do Seaborn . Por convenção, Seaborn é importado como sns:
Agora vamos executar novamente as mesmas duas linhas de antes (Figura 4-112):
ideia principal do Seaborn é fornecer comandos de alto nível para criar uma variedade de tipos de gráficos úteis para
exploração de dados estatísticos e até mesmo alguns ajustes de modelos estatísticos.
Vamos dar uma olhada em alguns conjuntos de dados e tipos de gráficos disponíveis no Seaborn. Observe que todos
os itens a seguir podem ser feitos usando comandos Matplotlib brutos (isso é, na verdade, o que o Seaborn faz nos
bastidores), mas a API do Seaborn é muito mais conveniente.
Histogramas, KDE e
densidades Muitas vezes, na visualização de dados estatísticos, tudo o que você deseja é traçar
histogramas e distribuições conjuntas de variáveis. Vimos que isso é relativamente simples no
Matplotlib (Figura 4-113):
Em [6]: dados = np.random.multivariate_normal([0, 0], [[5, 2], [2, 2]], tamanho=2000) dados =
pd.DataFrame(dados, colunas=['x ', 'você'])
Em vez de um histograma, podemos obter uma estimativa suave da distribuição usando uma
estimativa de densidade de kernel, que Seaborn faz com sns.kdeplot (Figura 4-114):
Em[8]: sns.distplot(dados['x'])
sns.distplot(dados['y']);
Se passarmos o conjunto de dados bidimensional completo para o kdeplot, obteremos uma visualização
bidimensional dos dados (Figura 4-116):
Em[9]: sns.kdeplot(dados);
Podemos ver a distribuição conjunta e as distribuições marginais juntas usando sns.jointplot. Para este gráfico,
definiremos o estilo para um fundo branco (Figura 4-117):
Figura 4-117. Um gráfico de distribuição conjunta com uma estimativa de densidade de kernel bidimensional
Existem outros parâmetros que podem ser passados para o jointplot – por exemplo, podemos
use um histograma de base hexagonal (Figura 4-118):
Figura 4-118. Um gráfico de distribuição conjunta com uma representação de compartimento hexagonal
Gráficos de pares
Quando você generaliza gráficos conjuntos para conjuntos de dados de dimensões maiores, você acaba com
parcelas de pares. Isto é muito útil para explorar correlações entre
dados, quando você deseja plotar todos os pares de valores uns contra os outros.
Demonstraremos isso com o conhecido conjunto de dados Iris, que lista medidas de pétalas
e sépalas de três espécies de íris:
Histogramas facetados
Às vezes, a melhor maneira de visualizar os dados é por meio de histogramas de subconjuntos. FacetGrid de Seaborn
torna isso extremamente simples. Veremos alguns dados que mostram a quantidade
que os funcionários do restaurante recebem em gorjetas com base em vários dados de indicadores (Figura 4-120):
Gráficos de fatores
Os gráficos de fatores também podem ser úteis para esse tipo de visualização. Isso permite que você
visualizar a distribuição de um parâmetro dentro dos compartimentos definidos por qualquer outro parâmetro
(Figura 4-121):
Figura 4-121. Um exemplo de gráfico de fator, comparando distribuições dados vários fatores discretos
Distribuições conjuntas
Semelhante ao gráfico de pares que vimos anteriormente, podemos usar sns.jointplot para mostrar a distribuição
conjunta entre diferentes conjuntos de dados, juntamente com as distribuições marginais associadas (Figura
4-122):
O gráfico conjunto pode até fazer algumas estimativas e regressões automáticas de densidade do kernel
(Figura 4-123):
Gráficos de barras
As séries temporais podem ser plotadas com sns.factorplot. No exemplo a seguir (visualizado
na Figura 4-124), usaremos os dados de Planetas que vimos pela primeira vez em “Agregação e
Agrupamento” na página 158:
Podemos aprender mais observando o método de descoberta de cada um desses planetas, conforme ilustrado
na Figura 4-125:
Figura 4-125. Número de planetas descobertos por ano e tipo (veja o apêndice online para obter uma figura
em escala real)
Para obter mais informações sobre plotagem com o Seaborn, consulte a documentação do Seaborn, um tutorial
e a galeria do Seaborn.
Aqui veremos como usar o Seaborn para ajudar a visualizar e compreender os resultados de finalização de uma
maratona. Eu extraí os dados de fontes na Web, agreguei-os e removi qualquer informação de identificação e
coloquei-os no GitHub, onde podem ser baixados (se você estiver interessado em usar Python para web scraping,
eu recomendaria Web Scraping com Python por Ryan Mitchell). Começaremos baixando os dados da Web e
carregando-os no Pandas:
Em[22]:
# !curl -O https:// raw.githubusercontent.com/ jakevdp/ marathon-data/
# master/ marathon-data.csv
Por padrão, o Pandas carregou as colunas de tempo como strings Python (tipo object); pudermos
veja isso observando o atributo dtypes do DataFrame:
Em[24]: data.dtypes
dados = pd.read_csv('marathon-data.csv',
conversores={'split':convert_time, 'final':convert_time})
dados.head()
Em[26]: data.dtypes
Isso parece muito melhor. Para fins de nossos utilitários de plotagem Seaborn, vamos a seguir
adicione colunas que fornecem os tempos em segundos:
Para ter uma ideia da aparência dos dados, podemos traçar um gráfico conjunto sobre os dados
(Figura 4-126):
A linha pontilhada mostra onde estaria o tempo de alguém se ele corresse a maratona em uma
ritmo perfeitamente constante. O fato de a distribuição estar acima disso indica (como você
poderia esperar) que a maioria das pessoas diminuísse a velocidade ao longo da maratona. Se você
correram competitivamente, você saberá que aqueles que fazem o oposto – correm mais rápido durante
na segunda metade da corrida - dizem que “dividiram negativamente” a corrida.
Vamos criar outra coluna nos dados, a fração dividida, que mede o grau
para o qual cada corredor divide negativamente ou positivamente a corrida:
Onde esta diferença de divisão é menor que zero, a pessoa divide negativamente a raça por isso
fração. Vamos fazer um gráfico de distribuição dessa fração dividida (Figura 4-127):
Figura 4-127. A distribuição de frações divididas; 0,0 indica um corredor que completou
o primeiro e o segundo tempo em tempos idênticos
Fora[31]: 251
Dos quase 40.000 participantes, apenas 250 pessoas dividiram negativamente seus
maratona.
Vamos ver se existe alguma correlação entre esta fração dividida e outras variáveis
capazes. Faremos isso usando uma pairgrid, que desenha gráficos de todas essas correlações
(Figura 4-128):
Em[32]:
g = sns.PairGrid(dados, vars=['idade', 'split_sec', 'final_sec', 'split_frac'],
matiz='gênero', paleta='RdBu_r')
g.map(plt.dispersão, alfa=0,8)
g.add_legend();
Parece que a fração dividida não se correlaciona particularmente com a idade, mas sim com o tempo
final: corredores mais rápidos tendem a ter tempos parciais mais próximos do par no seu tempo de
maratona. (Vemos aqui que Seaborn não é uma panacéia para os males do Matplotlib quando se
trata de estilos de plotagem: em particular, os rótulos do eixo x se sobrepõem. Como a saída é um
gráfico simples do Matplotlib, no entanto, os métodos em “Personalizando Ticks” na página 275 pode
ser usado para ajustar essas coisas, se desejado.)
A diferença entre homens e mulheres aqui é interessante. Vejamos o histograma de frações divididas
para esses dois grupos (Figura 4-129):
O interessante aqui é que há muito mais homens do que mulheres que estão perto de uma divisão
equilibrada! Isto quase parece algum tipo de distribuição bimodal entre homens e mulheres. Vamos ver
se conseguimos descobrir o que está acontecendo observando as distribuições em função da idade.
Uma boa maneira de comparar distribuições é usar um gráfico de violino (Figura 4.130):
Em[34]:
sns.violinplot("gênero", "split_frac", dados=dados,
paleta=["lightblue", "lightpink"]);
Vamos olhar um pouco mais fundo e comparar essas tramas de violino em função da idade. Bem
comece criando uma nova coluna na matriz que especifica a década de idade que cada
pessoa está (Figura 4-131):
Fora[35]:
idade gênero dividir final split_sec final_sec split_frac age_dec
0 33 M 01:05:38 02:08:51 30 3938,0 7731,0 -0,018756
1 32 M 01:06:26 02:09:28 3.986,0 7768,0 -0,026262 30
2 31 3 M 01:06:49 02:10:42 4009,0 7842,0 -0,022443 8025,0 30
38 M 01:06:16 02:13:45 3976,0 0,009097 30
4 31 M 01:06:32 02:13:59 3.992,0 8039,0 0,006842 30
Em[36]:
homens = (dados.gênero == 'M')
mulheres = (data.gender == 'W')
Figura 4-131. Um gráfico de violino mostrando a fração dividida por sexo e idade
Olhando para isto, podemos ver onde as distribuições de homens e mulheres diferem: a divisão
distribuições de homens na faixa dos 20 a 50 anos mostram uma pronunciada superdensidade em relação
divisões mais baixas quando comparadas com mulheres da mesma idade (ou de qualquer idade, nesse caso
matéria).
Também surpreendentemente, as mulheres de 80 anos parecem superar todos em termos de tempo parcial.
Provavelmente, isso se deve ao fato de estarmos estimando a distribuição a partir de números pequenos, já
que há apenas alguns corredores nessa faixa:
Fora[38]: 7
Voltando aos homens com divisões negativas: quem são esses corredores? Esta fração dividida está
correlacionada com o acabamento rápido? Podemos traçar isso com muita facilidade. Usaremos o regplot, que
ajustará automaticamente uma regressão linear aos dados (Figura 4-132):
Aparentemente, as pessoas com divisões rápidas são os corredores de elite que terminam em aproximadamente
15.000 segundos, ou cerca de 4 horas. Pessoas mais lentas do que isso têm muito menos probabilidade de ter
uma segunda divisão rápida.
Recursos adicionais
Recursos do Matplotlib Um
único capítulo de um livro nunca pode cobrir todos os recursos e tipos de gráficos disponíveis no Matplotlib.
Assim como acontece com outros pacotes que vimos, o uso liberal das funções de preenchimento de tabulação
e ajuda do IPython (consulte “Ajuda e documentação no IPython” na página 3) pode ser muito útil quando você
estiver explorando a API do Matplotlib. Além disso, a documentação online do Matplotlib pode ser uma referência
útil. Veja em particular o
Galeria Matplotlib vinculada a essa página: mostra miniaturas de centenas de tipos de gráficos diferentes,
cada um vinculado a uma página com o trecho de código Python usado para gerá-lo.
Dessa forma, você pode inspecionar visualmente e aprender sobre uma ampla variedade de diferentes
estilos de plotagem e técnicas de visualização.
Matplotlib seja a biblioteca de visualização Python mais proeminente, existem outras ferramentas mais
modernas que também valem a pena explorar. Mencionarei alguns deles brevemente aqui:
• Bokeh é uma biblioteca de visualização JavaScript com interface Python que cria visualizações
altamente interativas, capazes de lidar com conjuntos de dados muito grandes e/ou de streaming. O
frontend Python gera uma estrutura de dados JSON que pode ser interpretada pelo mecanismo
Bokeh JS.
• Plotly é o produto de código aberto homônimo da empresa Plotly e é semelhante em espírito ao Bokeh.
Por ser o principal produto de uma startup, o Plotly está recebendo um alto nível de esforço de
desenvolvimento. O uso da biblioteca é totalmente gratuito.
grandes e impressionantes. • Vega e Vega-Lite são representações gráficas declarativas e são o produto
de anos de pesquisa na linguagem fundamental da visualização de dados. A implementação de
renderização de referência é JavaScript, mas a API é independente de linguagem.
Há uma API Python em desenvolvimento no pacote Altair. Embora ainda não esteja maduro, estou
bastante entusiasmado com as possibilidades deste projeto de fornecer um ponto de referência
comum para visualização em Python e outras linguagens.
O espaço de visualização na comunidade Python é muito dinâmico e espero que esta lista fique
desatualizada assim que for publicada. Fique de olho no que vem por aí!
CAPÍTULO 5
Aprendizado de máquina
De muitas maneiras, o aprendizado de máquina é o principal meio pelo qual a ciência de dados
se manifesta para o mundo em geral. O aprendizado de máquina é onde essas habilidades
computacionais e algorítmicas da ciência de dados se encontram com o pensamento estatístico
da ciência de dados, e o resultado é uma coleção de abordagens para inferência e exploração de
dados que não tratam tanto de teoria eficaz, mas de computação eficaz.
O termo “aprendizado de máquina” às vezes é usado como se fosse uma espécie de pílula
mágica: aplique o aprendizado de máquina aos seus dados e todos os seus problemas serão resolvidos!
Como seria de esperar, a realidade raramente é tão simples. Embora esses métodos possam ser
incrivelmente poderosos, para serem eficazes eles devem ser abordados com uma compreensão
firme dos pontos fortes e fracos de cada método, bem como uma compreensão de conceitos
gerais, como viés e variância, sobreajuste e subajuste e muito mais.
Grande parte deste material foi extraído dos tutoriais e workshops do Scikit-Learn que ministrei
em diversas ocasiões na PyCon, SciPy, PyData e outras conferências. Qualquer
331
Machine Translated by Google
a clareza nas páginas seguintes provavelmente se deve aos muitos participantes do workshop e co-
instrutores que me deram feedback valioso sobre este material ao longo dos anos!
Finalmente, se você estiver buscando um tratamento mais abrangente ou técnico de qualquer um desses
assuntos, listei vários recursos e referências em “Recursos adicionais de aprendizado de máquina” na página
514.
Compreender a definição de problemas no aprendizado de máquina é essencial para usar essas ferramentas
de maneira eficaz e, portanto, começaremos com algumas categorizações amplas dos tipos de abordagens
que discutiremos aqui.
nível mais fundamental, o aprendizado de máquina pode ser categorizado em dois tipos principais: aprendizado
supervisionado e aprendizado não supervisionado.
A aprendizagem supervisionada envolve de alguma forma modelar a relação entre características medidas
dos dados e algum rótulo associado aos dados; uma vez determinado este modelo, ele pode ser usado para
aplicar rótulos a dados novos e desconhecidos. Isto é subdividido em tarefas de classificação e tarefas de
regressão: na classificação, os rótulos são categorias discretas, enquanto na regressão, os rótulos são
quantidades contínuas. Veremos exemplos de ambos os tipos de aprendizagem supervisionada na seção
seguinte.
A aprendizagem não supervisionada envolve modelar os recursos de um conjunto de dados sem referência a
qualquer rótulo e é frequentemente descrita como “deixar o conjunto de dados falar por si”. Esses modelos
incluem tarefas como agrupamento e redução de dimensionalidade. Algoritmos de cluster
identificam grupos distintos de dados, enquanto algoritmos de redução de dimensionalidade buscam representações
mais sucintas dos dados. Veremos exemplos de ambos os tipos de aprendizagem não supervisionada na seção
seguinte.
Além disso, existem os chamados métodos de aprendizagem semissupervisionados, que ficam em algum lugar entre
a aprendizagem supervisionada e a aprendizagem não supervisionada. Os métodos de aprendizagem
semissupervisionados costumam ser úteis quando apenas rótulos incompletos estão disponíveis.
ideias mais concretas, vamos dar uma olhada em alguns exemplos muito simples de tarefas de aprendizado de
máquina. Esses exemplos têm como objetivo fornecer uma visão geral intuitiva e não quantitativa dos tipos de
tarefas de aprendizado de máquina que veremos neste capítulo. Nas seções posteriores, entraremos em mais
detalhes sobre os modelos específicos e como eles são usados. Para uma prévia desses aspectos mais técnicos,
você pode encontrar a fonte Python que gera as figuras no apêndice online.
Primeiro daremos uma olhada em uma tarefa de classificação simples, na qual você recebe um conjunto de pontos
rotulados e deseja usá-los para classificar alguns pontos não rotulados.
Imagine que temos os dados mostrados na Figura 5-1 (o código usado para gerar esta figura, e todas as figuras desta
seção, estão disponíveis no apêndice online).
Aqui temos dados bidimensionais; isto é, temos duas características para cada ponto, representadas
pelas posições (x,y) dos pontos no plano. Além disso, temos um dos dois rótulos de classe para cada
ponto, aqui representados pelas cores dos pontos. A partir dessas características e rótulos, gostaríamos
de criar um modelo que nos permitirá decidir se um novo ponto deve ser rotulado como “azul” ou
“vermelho”.
Existem vários modelos possíveis para tal tarefa de classificação, mas aqui usaremos um modelo
extremamente simples. Faremos a suposição de que os dois grupos podem ser separados traçando uma
linha reta através do plano entre eles, de modo que os pontos de cada lado da linha caiam no mesmo
grupo. Aqui o modelo é uma versão quantitativa da afirmação “uma linha reta separa as classes”, enquanto
os parâmetros do modelo são os números específicos que descrevem a localização e a orientação dessa
linha para os nossos dados. Os valores ideais para esses parâmetros do modelo são aprendidos a partir
dos dados (este é o “aprendizado” no aprendizado de máquina), o que geralmente é chamado de
treinamento do modelo.
A Figura 5-2 é uma representação visual da aparência do modelo treinado para esses dados.
Agora que este modelo foi treinado, ele pode ser generalizado para dados novos e não rotulados. Por
outras palavras, podemos pegar num novo conjunto de dados, desenhar esta linha de modelo através
dele e atribuir rótulos aos novos pontos com base neste modelo. Este estágio é geralmente chamado de
previsão. Consulte a Figura 5-3.
Esta é a ideia básica de uma tarefa de classificação em aprendizado de máquina, onde “classificação”
indica que os dados possuem rótulos de classe discretos. À primeira vista isto pode parecer bastante
trivial: seria relativamente fácil simplesmente olhar para estes dados e traçar uma linha discriminatória
para realizar esta classificação. Um benefício da abordagem de aprendizado de máquina, entretanto, é
que ela pode generalizar para conjuntos de dados muito maiores em muito mais dimensões.
Por exemplo, isso é semelhante à tarefa de detecção automatizada de spam para e-mail; neste caso,
podemos usar os seguintes recursos e rótulos:
Para o conjunto de treinamento, esses rótulos podem ser determinados pela inspeção individual de uma
pequena amostra representativa de e-mails; para os emails restantes, o rótulo seria determinado usando
o modelo. Para um algoritmo de classificação adequadamente treinado com recursos bem construídos
(normalmente milhares ou milhões de palavras ou frases), esse tipo de abordagem pode ser muito
eficaz. Veremos um exemplo dessa classificação baseada em texto em “Em profundidade: classificação
Naive Bayes” na página 382.
Alguns algoritmos de classificação importantes que discutiremos com mais detalhes são Gaussian
ingênuo Bayes (veja “Em profundidade: Classificação Naive Bayes” na página 382), máquinas de
vetores de suporte (veja “Em profundidade: Máquinas de vetores de suporte” na página 405) e
classificação aleatória de florestas (consulte “Detalhes: Árvores de decisão e florestas aleatórias” na
página 421).
Em contraste com os rótulos discretos de um algoritmo de classificação, veremos a seguir uma tarefa
de regressão simples na qual os rótulos são quantidades contínuas.
Considere os dados mostrados na Figura 5-4, que consiste em um conjunto de pontos, cada um com um rótulo contínuo.
Tal como acontece com o exemplo de classificação, temos dados bidimensionais; isto é, existem dois recursos que
descrevem cada ponto de dados. A cor de cada ponto representa o rótulo contínuo para esse ponto.
Existem vários modelos de regressão possíveis que podemos usar para esse tipo de dados, mas aqui usaremos uma
regressão linear simples para prever os pontos. Este modelo de regressão linear simples assume que se tratarmos o rótulo
como uma terceira dimensão espacial, podemos ajustar um plano aos dados. Esta é uma generalização de nível superior
do conhecido problema de ajustar uma reta a dados com duas coordenadas.