Tópico 8 – Um pouco Mais de Visualização de Dados 📈

Aprender um pouco sobre transformação de dados e alguns exemplos a mais.

Resultados Esperados

  1. Aprender como transformar dados
  2. Aprender qual a melhor forma de ter dados sobre postos

Material Adaptado do DSC10 (UCSD)

#In: 
import numpy as np
import babypandas as bpd
import matplotlib.pyplot as plt
plt.style.use('ggplot')

Agenda

  • Distribuições.
  • Histogramas de densidade.
  • Parcelas sobrepostas.

Revisão: tipos de visualizações

O tipo de visualização que criamos depende dos tipos de variáveis ​​que estamos visualizando.

  • Gráfico de dispersão: numérico versus numérico.
  • Exemplo: peso x altura.
  • Gráfico de linhas: numérico sequencial (tempo) vs.
  • Exemplo: altura vs. tempo.
  • Gráfico de barras: categórico vs. numérico.
  • Exemplo: alturas de diferentes membros da família.
  • Histograma: distribuição numérica.

Observação: Podemos trocar as palavras “plot”, “chart” e “graph”; todos eles significam a mesma coisa.

Algumas visualizações ruins

Distribuições

Qual é a distribuição de uma variável?

  • A distribuição de uma variável consiste em todos os valores da variável que ocorrem nos dados, juntamente com suas frequências.
  • As distribuições ajudam você a entender:
    • Com que frequência uma variável assume um determinado valor?_
  • Ambas as variáveis ​​categóricas e numéricas têm distribuições.

Variáveis ​​categóricas

A distribuição de uma variável categórica pode ser exibida como uma tabela ou gráfico de barras, entre outras formas! Por exemplo, vejamos qual o tipo de ensino médio dos alunos de FCD.

#In: 
tipo_medio = bpd.DataFrame().assign(
    TipoEscola=['Privado', 'Público (Estadual)', 'Público (Municipal)', 'Público (Federal)'], 
    NumDiscentes=[15, 8, 3, 7]
)
tipo_medio
TipoEscolaNumDiscentes
0Privado15
1Público (Estadual)8
2Público (Municipal)3
3Público (Federal)7
#In: 
tipo_medio.plot(kind='barh', x='TipoEscola', y='NumDiscentes');

png

#In: 
tipo_medio.plot(kind='bar', x='TipoEscola', y='NumDiscentes');

png

Variáveis ​​numéricas

A distribuição de uma variável numérica nem sempre pode ser representada com precisão por um gráfico de barras. Por exemplo, vejamos o número de streams de cada uma das 200 músicas mais populares no Spotify nos EUA. 🎵

#In: 
charts = bpd.read_csv('https://raw.githubusercontent.com/flaviovdf/fcd/master/assets/07-DataViz/data/regional-us-daily-2023-01-21.csv')
charts = (charts.set_index('rank')
          .assign(million_streams = np.round(charts.get('streams')/1000000, 2))
          .get(['track_name', 'artist_names', 'streams', 'million_streams'])
         )
charts
track_nameartist_namesstreamsmillion_streams
rank
1FlowersMiley Cyrus33563613.36
2Kill BillSZA24794452.48
3Creepin' (with The Weeknd & 21 Savage)Metro Boomin, The Weeknd, 21 Savage13373201.34
4Superhero (Heroes & Villains) [with Future & C...Metro Boomin, Future, Chris Brown12352851.24
5Rich FlexDrake, 21 Savage11097041.11
...............
196Burn, Burn, BurnZach Bryan2677720.27
197LET GOCentral Cee2674010.27
198Major DistributionDrake, 21 Savage2669860.27
199Sun to MeZach Bryan2669680.27
200The Real Slim ShadyEminem2666980.27

200 rows × 4 columns

Para ver a distribuição do número de streams, precisamos agrupar pela coluna 'million_streams'.

#In: 
stream_counts = charts.groupby('million_streams').count()
stream_counts = stream_counts.assign(Count=stream_counts.get('track_name')).drop(columns=['track_name', 'artist_names', 'streams'])
stream_counts
Count
million_streams
0.2717
0.2820
0.2919
0.308
0.3114
......
1.111
1.241
1.341
2.481
3.361

51 rows × 1 columns

#In: 
stream_counts.plot(kind='bar', y='Count', figsize=(15,5));

png

  • Isso obscurece o fato de que as duas músicas principais são atípicas, com muito mais streams do que as outras músicas.

  • O eixo horizontal deve ser numérico (como uma reta numérica), não categórico. Deve haver mais espaço entre certas barras do que outras.

Histogramas

Histogramas mostram a distribuição de variáveis ​​numéricas sem normalizar

Em vez de um gráfico de barras, visualizaremos a distribuição de uma variável numérica com um histograma. Vamos ver como é um histograma de densidade para 'million_streams'. O que você percebe nessa visualização?

#In: 
# Ignore the code for right now.
charts.plot(
    kind='hist',
    y='million_streams',
    bins=np.arange(0, 4, 0.5),
    ec='w'  # borda branca
);

png

Primeira ideia chave por trás dos histogramas: binning 🗑️

  • Binning é o ato de contar o número de valores numéricos que se enquadram nos intervalos definidos por dois pontos finais. Esses intervalos são chamados de “caixas”.
  • Um valor cai em uma caixa se for maior ou igual ao ponto final esquerdo e menor que o ponto final direito.
  • [a, b): a está incluído, b não.
  • A largura de uma caixa é sua extremidade direita menos sua extremidade esquerda.
#In: 
from IPython.display import display, IFrame

def binning_animation():
    src="https://docs.google.com/presentation/d/e/2PACX-1vTnRGwEnKP2V-Z82DlxW1b1nMb2F0zWyrXIzFSpQx_8Wd3MFaf56y2_u3JrLwZ5SjWmfapL5BJLfsDG/embed?start=false&loop=false&delayms=60000"
    width=900
    height=307
    display(IFrame(src, width, height))
binning_animation()

Personalizando as caixas

  • Por padrão, o Python agrupará seus dados em 10 compartimentos de tamanhos iguais.
  • Você pode especificar outro número de compartimentos de tamanhos iguais definindo o argumento opcional bins igual a algum outro valor inteiro.
  • Você também pode especificar o início e os pontos finais do compartimento personalizado definindo bins iguais a uma sequência de pontos finais do compartimento.
  • Pode ser um array list ou numpy.
#In: 
charts.plot(
    kind='hist',
    y='million_streams',
    bins=20,
    ec='w'
);

png

#In: 
charts.plot(
    kind='hist', y='million_streams', density=True,
    bins=[0, 1, 2, 3, 4, 5],
    ec='w'
);

png

Nos dois histogramas acima, o que é diferente e o que é igual?

Observações

  • A forma geral de todos os dois histogramas é a mesma, independentemente dos compartimentos. Esta forma é chamada de inclinada à direita.
  • Mais compartimentos fornecem uma imagem mais precisa e granular da distribuição da variável 'million_streams'.
  • Os valores do eixo $y$ parecem mudar muito quando mudamos os compartimentos. Agarre-se a esse pensamento; veremos o porquê em breve.

Histogramas de densidade mostram a distribuição de variáveis ​​numéricas

Podemos também criar histogramas normalizados ou histogramas de densidade.

Ideia chave por trás dos histogramas de densidade: a área total é 1

  • Em um histograma de densidade, o eixo $y$ pode ser difícil de interpretar, mas foi projetado para dar ao histograma uma propriedade muito boa: \(\textbf{As barras de um histograma de densidade }\) \(\textbf{têm uma área total combinada de 1.}\)
  • Isso significa que a área de uma barra é igual à proporção de todos os pontos de dados que caem nessa caixa.
  • Proporções e percentagens representam a mesma coisa.
  • Uma proporção é um decimal entre 0 e 1, uma porcentagem está entre 0\% e 100\%.
  • A proporção 0,34 significa 34\%.

Cálculo de exemplo

#In: 
charts.plot(
    kind='hist',
    y='million_streams',
    density=True,
    bins=[0, 0.5, 1, 1.5, 2.5, 4],
    ec='w'
);

png

Com base neste histograma, que proporção das 200 músicas mais populares teve menos de meio milhão de streams?

Exemplo de cálculo

  • A altura da barra [0, 0.5) parece ser apenas 1,6.
  • A largura da caixa é 0,5 - 0 = 0,5.

  • Portanto, usando a fórmula da área de um retângulo,
\[\begin{align}\text{Area} &= \text{Altura} \times \text{Largura} \\ &= 1,6 \times 0,5 \\ e= 0,8 \end{align}\]
  • Como as áreas representam proporções, isso significa que a proporção das 200 músicas mais populares com menos de 0,5 milhão de streams foi de aproximadamente 0,8 (ou 80\%).

Verifique a matemática

#In: 
first_bin = charts[charts.get('million_streams') < 0.5].shape[0]
first_bin
159
#In: 
first_bin/200
0.795

Isso corresponde ao resultado que obtivemos. (Não exatamente, já que fizemos uma estimativa para a altura.)

Como calcular alturas em um histograma de densidade

Como a barra de um histograma é um retângulo, sua área é dada por

\[\text{Área} = \text{Altura} \times \text{Largura}\]

Que significa

\[\text{Altura} = \frac{\text{Área}}{\text{Largura}} = \frac{\text{Proporção (ou Porcentagem)}}{\text{Largura}}\]

Isso implica que as unidades de altura são “proporção por (unidade $x$-eixo)”. O eixo $y$ representa uma espécie de densidade, e é por isso que o chamamos de histograma de densidade.

#In: 
charts.plot(kind='hist', y='million_streams', density=True,
            bins=[0, 0.5, 1, 1.5, 2.5, 4],
            ec='w');
plt.ylabel('Densidade')
Text(0, 0.5, 'Densidade')

png

As unidades do eixo $y$ aqui são “proporção por milhão de fluxos”, já que o eixo $x$ representa milhões de fluxos.

  • Infelizmente, as unidades do eixo $y$ no histograma sempre são exibidas como “Frequência”. Isto está errado!
  • Podemos corrigir com plt.ylabel(...)

Verificação de conceito ✅

Suponha que criamos um histograma de densidade dos tamanhos dos sapatos das pessoas. 👟 Abaixo estão as caixas que escolhemos junto com suas alturas.

CaixaAltura da barra
[3, 7)0,05
[7, 10)0,1
[10, 12)0,15
[12, 16]$X$

Qual deve ser o valor de $X$ para que este seja um histograma válido?

A. 0,02 B. 0,05 C. 0,2 D. 0,5 E. 0.7             

Gráficos de barras vs. histogramas

Gráfico de barrasHistograma
Mostra a distribuição de uma variável categóricaMostra a distribuição de uma variável numérica
1 eixo categórico, 1 eixo numérico2 eixos numéricos
As barras têm larguras e espaçamentos arbitrários, mas iguaisO eixo horizontal é numérico e está em escala
Os comprimentos das barras são proporcionais à quantidade numérica de interesseA altura mede a densidade; as áreas são proporcionais à proporção (porcentagem) de indivíduos

🌟 Importante 🌟

Nesta aula, “histograma” sempre significará um “histograma de densidade”. Usaremos apenas histogramas de densidade.

Nota: É possível criar o que é chamado de histograma de frequência onde o eixo $y$ simplesmente representa uma contagem do número de valores em cada compartimento. Embora sejam mais fáceis de interpretar, os histogramas de frequência não têm a importante propriedade de que a área total é 1, portanto não podem ser conectados à probabilidade da mesma forma que os histogramas de densidade. Isso os torna muito menos úteis para cientistas de dados.

Gráficos sobrepostos

Novo conjunto de dados: populações de San Diego e San Jose ao longo do tempo

Os dados para ambas as cidades vêm de macrotrends.net.

#In: 
population = bpd.read_csv('https://raw.githubusercontent.com/flaviovdf/fcd/master/assets/08-MaisViz/data/sd-sj-2022.csv').set_index('date')
population
Pop SDGrowth SDPop SJGrowth SJ
date
197012090003.6910090004.34
197112520003.5610270001.78
197212970003.5910460001.85
197313440003.6210640001.72
197413920003.5710840001.88
...............
201932310000.5917830000.39
202032510000.6217910000.45
202132720000.6517990000.45
202232950000.7018090000.56
202333190000.7318210000.66

54 rows × 4 columns

Lembre-se: gráficos de linha

#In: 
population.plot(kind='line', y='Growth SD', 
                title='San Diego population growth rate', legend=False);

png

#In: 
population.plot(kind='line', y='Growth SJ', 
                title='San Jose population growth rate', legend=False);

png

Observe os argumentos opcionais title e legend. Alguns outros argumentos opcionais úteis são figsize, xlabel e ylabel. Existem many optional arguments.

Gráficos sobrepostos

Se y=column_name for omitido, todas colunas serão plotadas!

#In: 
population.plot(kind='line');

png

Por que existem apenas três linhas mostradas, mas quatro na legenda? 🤔

Selecionando várias colunas de uma vez

  • Para selecionar várias colunas, use .get([column_1, ..., column_k]).
  • Passar uma lista de rótulos de colunas para .get retorna um DataFrame.
  • .get([column_name]) retornará um DataFrame com apenas uma coluna!
#In: 
growths = population.get(['Growth SD', 'Growth SJ'])
growths
Growth SDGrowth SJ
date
19703.694.34
19713.561.78
19723.591.85
19733.621.72
19743.571.88
.........
20190.590.39
20200.620.45
20210.650.45
20220.700.56
20230.730.66

54 rows × 2 columns

#In: 
growths.plot(kind='line');

png

Para traçar vários gráficos de uma vez:

  • .get apenas as colunas que contêm informações relevantes para o seu gráfico.
  • Equivalentemente, .drop todas as colunas estranhas.
  • Especifique a coluna para o eixo $x$ (se não for o índice) em .plot(x=column_name).
  • Omita o argumento y. Então todas as outras colunas serão plotadas em um eixo $y$ compartilhado.

A mesma coisa funciona para 'barh', 'bar' e 'hist', mas não para 'scatter'.

Novo conjunto de dados: alturas das crianças e de seus pais 👪 📏

  • Os dados abaixo foram coletados no final de 1800 por Francis Galton.
  • Ele era eugenista e defensor do racismo científico, por isso coletou esses dados.
  • Hoje entendemos que a eugenia é imoral e que não há evidências científicas ou qualquer outra justificativa para o racismo.
  • Revisitaremos esse conjunto de dados posteriormente no curso.
  • Por enquanto, precisaremos apenas das colunas 'mother' e 'childHeight'.
#In: 
mother_child = bpd.read_csv('https://raw.githubusercontent.com/flaviovdf/fcd/master/assets/08-MaisViz/data/galton.csv').get(['mother', 'childHeight'])
mother_child
motherchildHeight
067.073.2
167.069.2
267.069.0
367.069.0
466.573.5
.........
92966.064.0
93066.062.0
93166.061.0
93263.066.5
93363.057.0

934 rows × 2 columns

Plotando histogramas sobrepostos

alpha controla o quão transparentes as barras são (alpha=1 é opaco, alpha=0 é transparente).

#In: 
height_bins = np.arange(55, 80, 2.5)
mother_child.plot(kind='hist', density=True, ec='w',
                  alpha=0.65, bins=height_bins);

png

Por que as crianças parecem muito mais altas que as mães?

Prática Extra

Tente responder a estas perguntas com base no histograma sobreposto.

  1. Qual proporção de crianças tinha entre 70 e 75 polegadas de altura?

  2. Qual proporção de mães tinha entre 60 e 63 polegadas de altura?

Respostas

Clique aqui para mostrar. Pergunta 1 A altura da barra $[70, 72.5)$ é em torno de $0,08$, o que significa que $0,08 \cdot 2,5 = 0,2$ de crianças tinham altura nesse intervalo. A altura da barra $[70, 72.5)$ é em torno de $0,02$, o que significa $0,02 \cdot 2,5 = 0,05$ de crianças tinham altura nesse intervalo. Assim, a proporção geral de crianças que tinham entre $70$ e $75$ polegadas de altura era de cerca de $0,20 + 0,05 = 0,25$, ou $25\%$. Para verificar nossa resposta, podemos executar heights[(heights.get('childHeight') >= 70) & (heights.get('childHeight') < 75)].shape[0] / heights.shape[0] Pergunta 2 Não podemos dizer. Poderíamos tentar dividir na proporção de mães em $[60, 62.5)$ e $[60, 62.5)$, mas não sabemos o último. Na ausência de qualquer informação adicional, não podemos inferir sobre a distribuição de valores dentro de um compartimento. Por exemplo, pode ser que todos no intervalo $[60, 62.5)$ realmente caiam no intervalo $[60, 62.5)$ - ou pode ser que ninguém caia!

Resumo, da próxima vez

Resumo

  • Histogramas (não gráficos de barras!) são usados ​​para exibir a distribuição de uma variável numérica.
  • Sempre usaremos histogramas de densidade.
  • Em histogramas de densidade, a área de uma barra representa a proporção (porcentagem) de valores dentro de seu compartimento.
  • A área total de todas as barras é 1 (100%).
  • Podemos sobrepor vários gráficos de linhas, gráficos de barras e histogramas uns sobre os outros para observar vários relacionamentos ou distribuições.

Próxima vez

  • Escrevendo nossas próprias funções.
  • Aplicar funções aos dados num DataFrame.