Pular para o conteúdo

    Capítulo 22, Intermediário

    Estatística: percentis, histogramas e correlação

    Resumir um conjunto de dados em poucos números, e descobrir se duas coisas variam juntas. O NumPy traz o essencial; o resto você encontra no pandas e no SciPy.

    Código deste capítulo: intermediario/cap22_estatistica.py

    Correlação entre duas variáveis

    A correlação mede o quanto duas variáveis andam juntas, de -1 (andam em sentidos opostos) a +1 (andam juntas), com 0 significando que não há relação linear. O np.corrcoef devolve a matriz de correlações, e a diagonal é sempre 1 (cada variável com ela mesma):

    intermediario/cap22_estatistica.pylinhas 10 a 18
    import numpy as np
    
    rng = np.random.default_rng(42)
    altura = rng.normal(170, 10, 500)
    peso = 0.9 * altura - 85 + rng.normal(0, 5, 500)
    
    matriz = np.corrcoef(altura, peso)
    print(matriz.round(2))
    print(0.8 < matriz[0, 1] < 0.95)
    
    Saída
    [[1.   0.86]
     [0.86 1.  ]]
    True
    

    Eu construí o peso a partir da altura mais um ruído, então a correlação alta é esperada (perto de 0,87, em teoria). Correlação não é causa, e dois números podem andar juntos por coincidência ou porque um terceiro fator move os dois.

    Histogramas

    O np.histogram conta quantos valores caem em cada faixa. Você escolhe as faixas, com um número de faixas iguais ou com os limites exatos. Cada faixa inclui o limite esquerdo e exclui o direito (menos a última, que inclui os dois):

    intermediario/cap22_estatistica.pylinhas 23 a 28
    notas = np.array([1, 2, 2, 3, 3, 3, 4, 4, 5])
    contagens, limites = np.histogram(notas, bins=[0, 2, 4, 6])
    print(contagens, limites)
    
    contagens, limites = np.histogram(altura, bins=5)
    print(contagens.sum(), len(limites))
    
    Saída
    [1 5 3] [0 2 4 6]
    500 6
    

    O número de limites é sempre um a mais que o de faixas, e a soma das contagens é o total de valores.

    Variações ao longo do tempo

    O np.diff calcula a diferença entre elementos consecutivos, e dividi-la pelo valor anterior dá a variação percentual. O np.cumsum acumula uma soma ao longo do array:

    intermediario/cap22_estatistica.pylinhas 33 a 35
    precos = np.array([100.0, 102.0, 99.0, 105.0])
    retornos = np.diff(precos) / precos[:-1]
    print(retornos.round(4), np.cumsum([1, 2, 3, 4]))
    
    Saída
    [ 0.02   -0.0294  0.0606] [ 1  3  6 10]
    

    O resultado tem um elemento a menos que a entrada, porque o primeiro preço não tem anterior.

    Agrupar com `bincount`

    O np.bincount conta ocorrências de cada inteiro, e com o parâmetro weights soma valores por grupo. É um GROUP BY do SQL em uma linha, para categorias numeradas de 0 em diante:

    intermediario/cap22_estatistica.pylinhas 40 a 42
    categorias = np.array([0, 1, 1, 2, 2, 2])
    valores = np.array([10, 20, 30, 1, 2, 3])
    print(np.bincount(categorias), np.bincount(categorias, weights=valores))
    
    Saída
    [1 2 3] [10. 50.  6.]
    

    Detectar valores atípicos (outliers)

    Uma regra clássica usa o intervalo interquartil (IQR, a distância entre o quartil de 25% e o de 75%): é atípico o que passa de 1,5 IQR além dos quartis. Ela resiste a extremos, ao contrário de uma regra baseada na média:

    intermediario/cap22_estatistica.pylinhas 47 a 51
    dados = np.array([10, 12, 11, 13, 12, 95])
    q1, q3 = np.percentile(dados, [25, 75])
    iqr = q3 - q1
    limite = q3 + 1.5 * iqr
    print(dados[dados > limite])
    
    Saída
    [95]
    

    Média e desvio dependem dos extremos

    O valor 95 desloca muito a média e o desvio padrão deste conjunto. Por isso, para achar outliers e para descrever dados assimétricos, eu prefiro mediana e quartis. O capítulo 14 mostrou o mesmo com salários.

    Exercício 1

    Detectar outliers dos dois lados

    Escreva outliers(x, k=1.5) que devolva os valores abaixo de Q1 - k*IQR ou acima de Q3 + k*IQR.