Data Science | Visualização de Dados

Compreenda melhor o seu dataset.

Fernando Matsumoto
Turing Talks
15 min readJun 23, 2019

--

Escrito por Fernando Matsumoto, Gabriela Fanucchi e Guilherme Fernandes.

Voltamos essa semana com mais um post do Turing Talks! Nas últimas semanas, temos falado bastante de programação e ciência de dados. Dando continuidade à essa sequência de posts, hoje, falaremos sobre visualização de dados.

Mas afinal, o que são dados e para que eles servem? Dados são conjuntos de informações, que podem ser quantitativas (numéricas) ou categóricas (por exemplo, o país em que uma pessoa mora ou a espécie de uma planta). Eles são muito utilizados em aplicações de Inteligência Artificial, para treinar algoritmos. Para isso, o cientista de dados precisa tratá-los da melhor maneira possível (de acordo com a finalidade e o tipo do dado) e visualizar o que está acontecendo para entender a informação que o dado passa. A-ha! Chegamos ao assunto do post. Após filtrar os dados, o cientista irá “plotá-los” da forma que lhe for conveniente para entendê-los.

Pois bem, nesse post abordaremos as principais formas de plotar os dados, além de algumas bibliotecas em Python que facilitam esse trabalho!

Dentre as bibliotecas que utilizaremos, uma já conhecemos: a matplotlib, que foi brevemente elucidada no Turing Talks #6 e será mostrada, também brevemente, nesse post. E a outra, que se chama seaborn, foi desenvolvida a partir da matplotlib e apresenta um nível de interface maior assim como mais opções de gráficos estatísticos. Para importar essas bibliotecas, usamos:

Gráficos Univariáveis

Histogramas

Os histogramas servem para mostrar a frequência de dados, tanto numéricos quanto categóricos. Podemos dizer que eles mostram uma contagem dos valores de uma variável. Para entender melhor o que isso significa, vamos analisar um histograma da idade dos passageiros do Titanic.

Nesse gráfico, o eixo x representa a idade e o eixo y, o número de pessoas com aquela idade. Dessa forma, o gráfico nos mostra que o navio transportava desde bebês até idosos, e que a maioria das pessoas tinha entre 20 e 40 anos. Analisando o gráfico, podemos perceber que o histograma se assemelha a um gráfico de barras. No entanto, em geral, não é possível criar um gráfico de barras para dados contínuos (imagine criar uma barra para cada valor real possível entre 0 e 80). Por isso, ao criar um histograma, o matplotlib divide os dados em intervalos de 10 anos (entre 0 e 10 anos, 10 e 20 anos, …). O que o histograma mostra é a quantidade de pessoas com idade em cada um desses intervalos. Para gerar um histograma, usamos a função plt.hist(x). Se quisermos controlar o número de intervalos usados pelo histograma, devemos usar o parâmetro bins. Para gerar o gráfico acima, utilizamos o dataset Titanic:

Distribuições e Densidades

Além do histograma, temos também o gráfico da densidade de uma feature (kde plot — kernel density estimation plot). Esse gráfico é uma versão “suavizada” do histograma. Note que o eixo y mostra uma estimativa da frequência relativa de cada valor. Em termos probabilísticos, trata-se de uma estimativa da função densidade de probabilidade da feature.

Também podemos mostrar a densidade e o histograma juntos usando o distplot:

Dados Categóricos

Um caso especial da contagem de dados é quando a variável é categórica, ou seja, ela assume um número limitado de valores possíveis. Isso ocorre, por exemplo, com o país de origem de uma pessoa. Nesse caso, dizemos que cada valor da variável (cada país) é uma da categoria.

Para o caso unidimensional, geralmente se deseja analisar o número de observações em cada categoria. Isso é bem similar a um histograma, salvo que ao invés de contar as observações em um intervalo de valores (quantas pessoas entre 0 e 10 anos), contamos elas por categorias (quantos homens?). Por exemplo, podemos analisar a distribuição de passageiros por sexo:

O gráfico mostra que aproximadamente dois terços dos passageiros eram homens.

Gráficos Multivariáveis

Gráficos de linha (Line Plots)

Em alguns datasets é interessante analisar as mudanças em uma variável com relação ao tempo, ou a similaridade com uma função contínua por exemplo. Nesses casos uma boa escolha é usar os gráficos de linha.

Esse tipo de gráfico é muito útil para funções contínuas (que de maneira simplificada, são funções cujo gráfico pode ser desenhado sem levantar o lápis do papel). Por exemplo, quando se quer representar a variação de temperaturas de uma determinada cidade em função do tempo. Vamos utilizar como exemplo o dataset Wine, que tem diversas características de vários vinhos (qualidade, pH, álcool, acidez, …). Vamos primeiramente tentar usar a função do matplotlib para plotar o pH em função da concentração de ácido cítrico no vinho.

Claramente a função que cria gráficos de linha não funcionou. Por quê? Há dois motivos:

  1. A função plt.plot() plota cada ponto na ordem em que aparece no dataset e liga pontos consecutivos por linhas. Isso é problemático porque os pontos nem sempre estão ordenados (ou seja, podemos ter pontos com ácido=0.2, 0.4, 0.3 ao invés de 0.2, 0.3, 0.4). Para plotar os dados corretamente, seria necessário ordenar os pontos por acidez.
  2. Os valores de ácido cítrico se repetem (por exemplo, 30 dos vinhos têm ácido=0.03). Isso significa que um plot desses dados criaria várias linhas verticais (conectando os vários vinhos com mesma concentração de ácido). Uma opção nesse caso é considerar a média do pH de vinhos com o mesmo valor de ácido.

A função sns.lineplot() faz essas duas tarefas automaticamente:

Esse gráfico mostra a relação entre a concentração de ácido cítrico e o pH. Como os pontos do gráfico são apenas uma média dos pontos do dataset original, podemos dizer que o pH mostrado no gráfico é apenas uma estimativa. Assim, há uma certa incerteza em torno desses valores. Essa incerteza é expressa pela área sombreada em azul (mais rigorosamente, esse sombreado representa o intervalo de confiança de 95% em torno da média).

Também é possível adicionar mais informações nos gráficos de linha, sobrepondo linhas de cores e padrões diferentes. Abaixo, mostramos um plot do dataset Iris, do comprimento pela largura das pétalas. A cor e estilo das linhas são determinados pela espécie da flor.

Nos gráficos acima, pudemos ter uma ideia da distribuição de dados. Um passo a mais seria começar a prever os dados, que é o que veremos mais a frente. No entanto, já podemos ter uma ideia de alguns modelos simples usando o seaborn. A função sns.lmplot() plota os pontos do dataset, assim como uma regressão linear desses pontos, ou seja, a reta que está mais “próxima” dos dados (o significado preciso disso ficará mais claro daqui a algumas semanas).

Heat Maps

Heat map, ou mapa de calor, é uma representação por cores da intensidade dos valores de uma matriz de entrada. Comumente em uma análise de dados essa matriz é a matriz de correlação das features.

A correlação é uma medida estatística entre duas variáveis contida no intervalo -1 e 1, a qual indica se uma variável descreve bem a outra por uma relação linear. Para valores próximos de 1, dizemos que as variáveis são positivamente correlacionadas e para valores próximos de -1, dizemos que estão negativamente correlacionadas. Nesse último caso a relação é inversamente proporcional.

Sabendo de features altamente correlacionadas, podemos criar modelos de machine learning mais robustos (apresentam taxa de acerto em previsões maiores), uma vez que entendemos melhor como o dataset se comporta. (Spoiler: isso é tópico para um próximo Turing Talks.)

Para obter a matriz de correlação de um dataframe, podemos utilizar o método df.corr() e, para plotar essa matriz, usamos sns.heatmap(). Na matriz abaixo, do dataset Wine, vemos que as features density e fixed acidity tem uma correlação positiva alta (o quadrado correspondente a essas duas features é quase branco). Por outro lado, a correlação entre pH e fixed acidity é negativa alta (o quadrado correspondente é quase preto). Geralmente, para decidir quais features são mais úteis, o que importa é se a correlação é alta ou baixa, e não negativa ou positiva. Assim, se quisermos prever a qualidade do vinho, queremos features que tenham correlação alta com a qualidade. Essas features são, principalmente, álcohol e volatile acidity. As outras features têm correlação relativamente próxima de zero, em comparação com essas duas.

Gráficos de Dispersão (Scatter Plots)

Nos gráficos de dispersão, os pontos representam o valor das observações em duas variáveis, uma medida ao longo do eixo x e a outra ao longo do eixo y. Podemos usar um scatter plot para ver como duas features numéricas estão relacionadas. Como exemplo, vamos utilizar o dataset Iris (o mesmo que usamos no post de bibliotecas). Nesse dataset, temos o comprimento e largura das pétalas e sépalas de várias flores, assim como a espécie de cada flor. Abaixo temos um scatter plot do comprimento pela largura das pétalas de uma flor, criado com o código:

Podemos perceber que, como esperado, quanto maior a largura da pétala (eixo x), maior o comprimento da pétala (eixo y). De fato, a relação parece bem linear, de forma que, para um problema de regressão, um modelo linear funcionaria bem. Podemos extrair mais informações desse gráfico se identificarmos também outras características da flor no gráfico. Para isso, usamos os seguintes parâmetros da função sns.scatterplot():

Os parâmetros hue, size e style controlam a cor, tamanho e estilo de cada ponto com base nas features dos dados. Por exemplo, para colorir cada ponto com base na espécie da flor, podemos usar:

Esse novo gráfico nos mostra que as flores menores são da espécie Iris setosa, enquanto as maiores são Iris virginica. Percebemos também que, para um problema de classificação (em que queremos determinar a espécie de uma flor com base em suas características), esse problema parece ser relativamente simples. As flores Iris setosa são linearmente separáveis das outras duas (podemos separá-las usando uma reta) e a espécie parece seguir uma relação simples com o tamanho da flor.

Uma outra variação desse mesmo plot é usando os parâmetros size e style para gerar um scatter plot do comprimento pela largura das pétalas, em que o tamanho dos pontos é proporcional à largura das sépalas e o estilo (círculo, x ou quadrado) depende da espécie.

Joint Plots

Às vezes, é útil ver tanto as relações entre duas features como as características de cada feature individualmente. Para isso, usamos o joint plot, que mostra a distribuição de duas features entre si e individualmente. A forma mais básica mostra um scatterplot e os histogramas:

Mexendo no parâmetro kind, podemos obter outros gráficos. Por exemplo, usando kind='kde', os plots são de densidade (tanto em uma variável, quanto em duas):

Pair Plots

Ao invés de plotar os gráficos para apenas algumas das features, podemos analisar todos os dados de uma vez. Para nos auxiliar nessa tarefa, a biblioteca seaborn fornece a função pairplot(), que plota a distribuição de cada feature, assim como as relações entre pares de features (na forma de scatter plots):

O pairplot pode utilizar tanto histogramas como densidades. O padrão é plotar densidades quando o parâmetro hue é passado e histogramas caso contrário. Para controlar o tipo de plot, podemos passar o parâmetro diag_kind='hist' ou diag_kind='kde'.

Dados Categóricos

Ao lidar com dados categóricos, temos dois tipos de gráficos: aqueles que se assemelham aos scatter plots (stripplot e swarmplot) e aqueles que estimam a função de densidade dos dados (boxplot e violinplot). Esses gráficos colocam uma feature categórica em um dos eixos e uma feature numérica no outro.

Stripplots

Se quiséssemos ver a distribuição conjunta de uma feature categórica por uma feature numérica, a nossa primeira ideia pode ser simplesmente fazer um scatterplot. Por exemplo, podemos fazer um scatterplot do titanic entre a variável Survived, que indica se o passageiro sobreviveu, e a variável Fare, que indica o quanto ele pagou. Nos próximos exemplos, vamos usar apenas uma parte do dataset do titanic (o motivo para isso será esclarecido mais tarde). O resultado disso pode ser visto abaixo:

É fácil perceber que esse gráfico não é muito útil. Há tantos pontos acumulados, um em cima do outro, que não sabemos a real distribuição dos dados. Uma modo de arrumar isso seria diminuir a sobreposição dos pontos, introduzindo um “ruído” no eixo horizontal, ou seja, fazendo com que os pontos se movam aleatóriamente na horizontal. Esse tipo de plot se chama stripplot.

O gráfico não está completamente claro, pois há muitos pontos sobrepostos, mas ele parece indicar que as pessoas que sobreviveram (Survived=1) pagaram, em geral, mais caro para estar no Titanic.

Swarmplots

No stripplot, podemos ver que ainda há sobreposição dos pontos. O swarmplot (também chamado de “beeswarm”) elimina essa sobreposição. Isso fornece uma melhor visualização da distribuição dos valores, mas não performa bem com um número grande de observações.

Com o swarmplot, fica ainda mais claro que as pessoas que sobreviveram, em geral, pagaram mais, visto que há muito mais pontos acumulados em baixo do lado esquerdo do que do lado direito.

Apesar desse gráfico estar melhor que o anterior, podemos ver que os 200 pontos desse dataset já estão chegando perto do limite do swarmplot, como evidenciado pela sobreposição de alguns pontos no canto inferior esquerdo. Os problemas desses plots com datasets grandes ficam ainda mais claros se utilizarmos o dataset inteiro do Titanic:

Stripplot com todos os dados do Titanic
Swarmplot com todos os dados do Titanic

Boxplot

Um boxplot é um diagrama das features de um dataset. Essa representação gráfica guarda 5 informações principais sobre a distribuição dos dados, são elas:

  • Mediana
  • Primeiro quartil (Q1)
  • Terceiro quartil (Q3)
  • Limite inferior: Q1–1.5*(Q3-Q1)
  • Limite superior: Q3 + 1.5*(Q3-Q1)

No boxplot temos 3 quartis, que dividem o dataset em 4 regiões: o primeiro quartil (Q1) representa o número tal que abaixo dele estejam 25% dos dados, e por conseguinte o segundo quartil (Q2) indica o valor de forma que abaixo dele estejam 50% dos dados o que é equivalente à mediana, e por fim, o terceiro quartil (Q3) está acima de 75% dos dados.

Os outliers indicam possivelmente erros nos dados (dados discrepantes), uma vez que dista mais que os limites do intervalo esperado, e portanto, podem ser desconsiderados.

Com isso, o boxplot fornece a ideia de posição, dispersão, assimetria, caudas e dados discrepantes. As posições dos quartis fornecem uma noção da assimetria da distribuição dos dados e o comprimento dos limites indicam comprimento das caudas.

Para elucidar essas características, utilizaremos o dataset Quarteto de Anscombe, que foi desenvolvido pelo estatístico Francis Anscombe. É um conjunto de dados que compreende outros quatro datasets, cada um contendo 11 pares (x,y).

O que é curioso sobre esses dados é que os quatro dados comprimidos apresentam as mesmas descrições estatísticas (soma, média, desvio padrão, etc…), mas são completamente diferentes quando visualizados graficamente.

Os dados do dataset Quarteto de Anscombe.

É possível acessar esse dataset pelo seaborn, que retorna os dados em formato de pandas dataframe.

Para a feature x temos que o boxplot de I, II e III são iguais já que as observações são as mesmas. Além disso, podemos notar a simetria nos quartis e mesmo comprimento dos limites (caudas), isso nos informa que o número de observações e os valores da features são simétricos com relação a mediana.

Já para IV é interessante notar que os quartis e os limites coincidem com a mediana, em linhas gerais os valores estão em sua maioria próximos a mediana, e a observação do dataset IV acima do limite superior é um outlier.

Já para a variável y, podemos observar em II a assimetria dos quartis e dos limites. Como o terceiro quartil (e o limite superior) estão próximos da mediana, podemos concluir que os dados acima da mediana estão concentrados mais próximos da dela. Por outro lado, o primeiro quartil e o limite inferior estão relativamente longe da mediana, nos levando a concluir que os dados abaixo da mediana estão mais bem distribuídos.

Violinplot

Esses gráficos juntam o boxplot com uma curva de densidade dos dados. O nome do gráfico (violinplot) vem do formato dos plots gerados, como podemos ver abaixo:

Essa informação diz mais sobre o dataset do que uma análise somente com boxplots. Isso é perceptível na imagem abaixo, em que os dados originais (à esquerda) estão variando significativamente. No entanto, os boxplots continuam iguais, ou seja, as estatísticas (mediana, quartis) dos dados não estão variando. Já os violinplots conseguem capturar a variação na distribuição dos dados.

Porém, como os violinplots precisam estimar a distribuição do dataset não é o ideal utilizá-los quando se dispõe de uma base de dados muito pequena. Nesse cenário é mais conveniente o uso de gráficos como o stripplot e swarmplot, que mostram os dados “crus”.

Violinplots para o dataset Anscombe

Com o seguinte comando geramos o gráfico:

Agora contendo a distribuição da variável em um diagrama com o boxplot podemos confirmar as afirmações sobre a simetria de x verificadas apenas com o boxplot. Enquanto I, II e III possuem a mesma distribuição simétrica, IV apresenta uma maior concentração de observações no valor médio.

Note que nessa representação é mais fácil identificar as caudas.

Com relação a feature y do dataset II, também podemos confirmar a assimetria e concentração de observações acima da mediana, enquanto os dados abaixo da mediana estão mais dispersos. Dizemos então que há uma cauda inferior nesses dados, em vista do formato da distribuição.

Conclusão

Chegamos ao final de mais um Turing Talks. Com essas ferramentas de visualização de dados agora é possível concluir muito mais sobre os dados.

Isso abre um leque para novas possibilidades, como uma análise estatística mais aprofundada dos dados e uma série de modelos de machine learning. Caso a utilização das funções apresentadas não tenha ficado claro, recomendamos que vocês leiam a documentação do seaborn e do matplotlib, ou comentem abaixo no post.

Nas próximas semanas continuaremos com os Turing Talks com mais assuntos para você ficar por dentro do mundo da IA! Se quiser saber mais siga também nossa página no Facebook e fique ligado no que postamos por lá.

Por hoje é só!

Abraços

--

--