Testes de Hipóteses

Entendendo valores-p

Resultados Esperados

  1. Entender o conceito de um teste de hipótese
  2. Entender o valor-p
  3. Saber realizar e interpretar testes de hipóteses
  4. Saber realizar e interpretar valores-p

Sumário

  1. Introdução
  2. Exemplo 1: Juri no Alabama
  3. Ideia de Testes de Hipóteses
  4. Exemplo 2 – Um outro Juri
  5. Caso 3. Dados Reais
  6. Teste de Permutação
  7. Animação

#In: 
# -*- coding: utf8

from scipy import stats as ss

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
#In: 
plt.style.use('seaborn-colorblind')

plt.rcParams['figure.figsize']  = (16, 10)
plt.rcParams['axes.labelsize']  = 20
plt.rcParams['axes.titlesize']  = 20
plt.rcParams['legend.fontsize'] = 20
plt.rcParams['xtick.labelsize'] = 20
plt.rcParams['ytick.labelsize'] = 20
plt.rcParams['lines.linewidth'] = 4
#In: 
plt.ion()
#In: 
def despine(ax=None):
    if ax is None:
        ax = plt.gca()
    # Hide the right and top spines
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)

    # Only show ticks on the left and bottom spines
    ax.yaxis.set_ticks_position('left')
    ax.xaxis.set_ticks_position('bottom')

Introdução

Este é o primeiro notbeook com base em testes de hipóteses. Para entender tal mundo, temos que cobrir:

  1. Testes quando sabemos da população
  2. Testes quando não sabemos da população
  3. Erros e Poder de Teste (próximo notebook)
  4. Causalidade (próximo notebook)

Assim como no mundo de intervalos de confiânça, a nossa ideia aqui é ter algum garantia estatística para fazer afirmações quando temos apenas uma amostra. Note que tal amostra, idealmente, vai ser grande o suficiente para estimar com menor viés alguns parâmetros como a média e o desvio padrão da população.

Quando testamos hipóteses queremos responder perguntas como: “Tal cenário é factível quando comparamos com uma hipótese nula?”. Esta hipótese nula representa um modelo probabílistico da população. No geral, queremos rejeitar a mesma indicando que não temos evidências de que os dados seguem tal modelo. Modelos, para fins deste notebook, quer dizer formas de amostrar a população. Imagine que uma moeda é jogada para cima 100 vezes e cai em caras 99 vezes. Uma hipótese nula seria: “A moeda é justa!”. Qual a chance de uma moeda justa cair em caras 99 vezes?

#In: 
x = np.arange(0, 101) # Valores no eixo x
prob_binom = ss.distributions.binom.pmf(x, 100, 0.5)
plt.plot(x, prob_binom, 'o')
plt.plot([99], prob_binom[99], 'ro')
plt.text(99, 0.0018, 'Aqui', horizontalalignment='center')
plt.xlabel('Num Caras - x')
plt.ylabel('P(sair x caras)')
plt.title('Chance de sair 99 caras (pontinho vermelho!)')
despine()

png

Quase zero! Porém, é fácil falar de moedas. Foi assim também quando estudamos o IC. Vamos explorar outros casos.

Exemplo 1: Juri no Alabama

No início dos anos 1960, no Condado de Talladega, no Alabama, um negro chamado Robert Swain foi condenado por à morte por estuprar uma mulher branca. Ele recorreu da sentença, citando entre outros fatores o júri todo branco. Na época, apenas homens com 21 anos ou mais eram autorizados a servir em júris no condado de Talladega. No condado, 26% dos jurados elegíveis eram negros. No juri final, havia apenas 8 negros entre os 100 selecionados para o painel de jurados no julgamento de Swain.

Nossa pergunta: Qual é a probabilidade (chance) de serem selecionados 8 indíviduos negros?

Para nos ajudar com este exemplo, o código amostra proporções de uma população. O mesmo gera 10,000 amostras ($n$) de uma população de tamanho pop_size. Tais amostras são geradas sem reposição. Além do mais, o código assume que:

  1. [0, $pop_size * prop$) pertencem a um grupo.
  2. [$pop_size * prop$, $pop_size$) pertencem a outro grupo.

Ou seja, em uma população de 10 (pop_size) pessoas. Caso a proporção seja 0.2 (prop). A população tem a seguinte cara:

[G1, G1, G2, G2, G2, G2, G2, G2, G2, G2].

A ideia do cógido é responder: Ao realizar amostras uniformes da população acima, quantas pessoas do tipo G1 e do tipo G2 são amostradas. Para isto, realizamos 10,0000 amostras.

#In: 
def sample_proportion(pop_size, prop, n=10000):
    '''
    Amostra proporções de uma população.
    
    Parâmetros
    ----------
    pop_size: int, tamanho da população
    prop: double, entre 0 e 1
    n: int, número de amostras
    '''
    assert(prop >= 0)
    assert(prop <= 1)
    
    grupo = pop_size * prop
    resultados = np.zeros(n)
    for i in range(n):
        sample = np.random.randint(0, pop_size, 100)
        resultados[i] = np.sum(sample < grupo)
    return resultados

Vamos ver agora qual é a cara de 10,000 amostras da cidade de Talladega. Vamos assumir que a cidade tem uma população de 100,000 habitantes. Tal número não importa muito para o exemplo, estamos olhando amostras. Podemos ajustar com a real.

O gráfico abaixo mostra no eixo-x o número de pessoas negras em cada amostra uniforme. Realizamos 10,000 delas. No eixo-y, a quantidade de amostras com aquele percentua. Agora responda, qual a chance de sair 8 pessoas apenas?

#In: 
proporcoes = sample_proportion(pop_size=100000, prop=0.26)
bins = np.linspace(1, 100, 100) + 0.5
plt.hist(proporcoes, bins=bins, edgecolor='k')
plt.xlim(0, 52)
plt.ylabel('Numero de Amostras de Tamanho 10k')
plt.xlabel('Número no Grupo')
plt.plot([8], [0], 'ro', ms=15)
despine()

png

Podemos usar 5\% de chance para nos ajudar. É assim que funciona os testes. Com 5\% de chances, saiem pelo menos 19 pessoas negras. Então estamos abaixo disto, bem raro!

#In: 
np.percentile(proporcoes, 5)
19.0

Este exemplo, assim como o próximo, embora não assuma nada sobre a população, assume uma hipótese nula de seleção uniforme. Isto é, qual é a probabilidade de ocorre o valor que observamos em uma seleção uniforme?!

Ideia de Testes de Hipóteses

  1. Dado um valor observado $t_{obs}.$
  2. Qual é a chance deste valor em uma hipótese (modelo) nulo?!

No exemplo acima $t_{obs}=8$.

Exemplo 2 – Um outro Juri

In 2010, the American Civil Liberties Union (ACLU) of Northern California presented a report on jury selection in Alameda County, California. The report concluded that certain ethnic groups are underrepresented among jury panelists in Alameda County, and suggested some reforms of the process by which eligible jurors are assigned to panels. In this section, we will perform our own analysis of the data and examine some questions that arise as a result.

Aqui temos um outro exemplo de juri. Neste caso, temos diferentes grupos raciais. Como podemos falar algo dos grupos? Precisamos de uma estatística de teste. Portanto, vamos usar a total variation distance.

\[TVD(p, q) = \sum_{i=0}^{n-1} abs(p_i - q_i)\]

p e q aqui são vetores com proporções. Cada vetor deve somar 1. Quão maior a TVD, maior a diferença entre as proporções dos dois vetores! Quando p == q, temos TVD == 0.

#In: 
def total_variation(p, q):
    '''
    Computa a total variation distance com base em dois vetore, p e q
    
    Parâmetros
    ----------
    p: vetor de probabilidades de tamanho n
    q: vetor de probabilidades de tamanho n
    '''
    return np.sum(np.abs(p - q)) / 2

Nossos dados. No juri, temos as seguintes proporções em cada raça.

#In: 
idx = ['Asian', 'Black', 'Latino', 'White', 'Other']
df = pd.DataFrame(index=idx)
df['pop'] = [0.15, 0.18, 0.12, 0.54, 0.01]
df['sample'] = [0.26, 0.08, 0.08, 0.54, 0.04]
df
popsample
Asian0.150.26
Black0.180.08
Latino0.120.08
White0.540.54
Other0.010.04
#In: 
df.plot.bar()
plt.ylabel('Propopção')
plt.ylabel('Grupo')
despine()

png

Vamos comparar com uma amostra aleatória! Para isto, vou amostrar cada grupo por vez. Note a diferença.

#In: 
N = 1453
uma_amostra = []
for g in df.index:
    p = df.loc[g]['pop']
    s = sample_proportion(N, p, 1)[0]
    uma_amostra.append(s/100)
#In: 
df['1random'] = uma_amostra
df.plot.bar()
plt.ylabel('Propopção')
plt.ylabel('Grupo')
despine()

png

Agora compare o TVD nos dados e na amostra aleatória!

#In: 
total_variation(df['1random'], df['pop'])
0.029999999999999995
#In: 
total_variation(df['sample'], df['pop'])
0.14

Para realizar o teste, fazemos 10,000 amostras e comparamos os TVDs! O código abaixo guarda o resultado de cada amostra em uma linha de uma matriz.

#In: 
N = 1453
A = np.zeros(shape=(10000, len(df.index)))
for i, g in enumerate(df.index):
    p = df.loc[g]['pop']
    A[:, i] = sample_proportion(N, p) / 100
#In: 
A
array([[0.18, 0.25, 0.15, 0.51, 0.01],
       [0.12, 0.16, 0.17, 0.52, 0.05],
       [0.16, 0.27, 0.14, 0.54, 0.01],
       ...,
       [0.13, 0.18, 0.09, 0.54, 0.02],
       [0.18, 0.15, 0.14, 0.48, 0.  ],
       [0.13, 0.21, 0.19, 0.53, 0.01]])

Agora o histograma da TVD. O ponto vermelho mostra o valor que observamos, as barras mostram as diferentes amostras. Novamente, bastante raro tal valor. Rejeitamos a hipótese nula e indicamos que os dados não foram selecionados de forma uniforme!

#In: 
all_distances = []
for i in range(A.shape[0]):
    all_distances.append(total_variation(df['pop'], A[i]))
#In: 
plt.hist(all_distances, bins=30, edgecolor='k')
plt.ylabel('Numero de Amostras de Tamanho 10k')
plt.xlabel('Total Variation Distance')
plt.plot([0.14], [0], 'ro', ms=15)
despine()

png

#In: 
np.percentile(all_distances, 97.5)
0.12000000000000001

Caso 3. Dados Reais

Agora, finalmente vamos assumir um caso com dados reais. Em particular, vamos comparar salários de dois times da NBA. O dataframe se encontra abaixo. Vamos focar nos times:

  1. Houston Rockets
  2. Cleveland Cavaliers

Diferente do exemplo anterior, simular aqui vair ser um pouco mais complicado. É mais complicado assumir uma população para gerar amostras. Portanto vamos fazer uso de testes de permutação. Inicialmente, vamos explorar os dados (abaixo)

#In: 
df = pd.read_csv('https://media.githubusercontent.com/media/icd-ufmg/material/master/aulas/11-Hipoteses/nba_salaries.csv')
df.head()
PLAYERPOSITIONTEAMSALARY
0Paul MillsapPFAtlanta Hawks18.671659
1Al HorfordCAtlanta Hawks12.000000
2Tiago SplitterCAtlanta Hawks9.756250
3Jeff TeaguePGAtlanta Hawks8.000000
4Kyle KorverSGAtlanta Hawks5.746479
#In: 
df = df[df['TEAM'].isin(['Houston Rockets', 'Cleveland Cavaliers'])]
df.head()
PLAYERPOSITIONTEAMSALARY
72LeBron JamesSFCleveland Cavaliers22.970500
73Kevin LovePFCleveland Cavaliers19.689000
74Kyrie IrvingPGCleveland Cavaliers16.407501
75Tristan ThompsonCCleveland Cavaliers14.260870
76Brendan HaywoodCCleveland Cavaliers10.522500

Vamos pegar o salário médio de cada time. Observe que teremos uma diferença de mais ou menos 3 milhões e doláres. Tal estatística será a nossa $t_{obs}$. Antes disso, vamos criar um filtro (vetor de booleanos) para o Houston.

#In: 
filtro = df['TEAM'] == 'Houston Rockets'

Agora o salário do Houston

#In: 
filtro = df['TEAM'] == 'Houston Rockets'
df[filtro]['SALARY'].mean()
7.107153083333334

Do não Houston, ou seja, Cleveland.

#In: 
df[~filtro]['SALARY'].mean()
10.231241200000001

Por fim, nossa estatística observada.

#In: 
t_obs = df[~filtro]['SALARY'].mean() - df[filtro]['SALARY'].mean()
t_obs
3.124088116666667

Teste de Permutação

Embora não estamos comparando proporções como nos exemplos anteriores, podemos sim brincar um pouco de simulação. Vamos assumir um seguinte modelo nulo. A probabilidade de um jogador escolher (ou for contratado) em um time para jogar é igual para os dois times. Ou seja, vamos dizer que a associação Jogador x Time é uniformimente aleatória! Como fazemos isto? Basta fazer um shuffle no filtro acima.

Assumindo apenas 5 jogadores. Os dados reais são:

  1. Nomes: [J1, J2, J3, J4, J5]
  2. Salários: [S1, S2, S3, S4, S5]
  3. Times: [T1, T1, T2, T2, T2]

Vamos manter os nomes e os salários fixos. Ao fazer um shuffle (embaralhamento) nos times, temos:

  1. Nomes: [J1, J2, J3, J4, J5]
  2. Salários: [S1, S2, S3, S4, S5]
  3. Times: [T1, T2, T2, T2, T1]

Esta é nossa hipótese nula! Vamos realizar uma!

#In: 
np.random.shuffle(filtro.values)
diff = df[~filtro]['SALARY'].mean() - df[filtro]['SALARY'].mean()
diff
-2.9790110833333374

Note acima que temos uma diferença de salários diferente do que observamos. Vamos agora gerar 10,000!

#In: 
N = 10000
diferencas = np.zeros(N)
for i in range(N):
    np.random.shuffle(filtro.values)
    diff = df[~filtro]['SALARY'].mean() - df[filtro]['SALARY'].mean()
    diferencas[i] = diff

Na figura abaixo mostramos os resultados do mundo simulado. Note que em 16% dos casos temos diferença de salários mais extremas do que $t_{obs}$. Isto é uma chance bem alta! Nos exemplos anteriores tinhamos valores bem menores. Ou seja, não rejeitamos a hipótese nula (modelo simulado). A variação dos salários pode ser explicado pelo acaso.

#In: 
plt.hist(diferencas, bins=50, edgecolor='k')
plt.xlabel('Diferença na Permutação')
plt.ylabel('Pr(diff)')
plt.vlines(t_obs, 0, 0.14, color='red')
plt.text(t_obs+1, 0.10, '$16\%$ dos casos')
despine()
plt.show()

png

Animação

#In: 
from IPython.display import HTML
from matplotlib import animation
#In: 
def update_hist(num, data):
    plt.cla()
    plt.hist(data[0:100 * (num+1)], bins=50,
             density=True, edgecolor='k')
    plt.xlabel('Diferença na Permutação')
    plt.ylabel('Pr(diff)')
    despine()
#In: 
fig = plt.figure()
ani = animation.FuncAnimation(fig, update_hist, 30, fargs=(diferencas, ))
HTML(ani.to_html5_video())

png