Rastreando as suposições feitas pela função ttest_ind () do SciPy

8

Estou tentando escrever meu próprio código Python para calcular estatísticas t e valores-p para um e dois testes t independentes de cauda. Eu posso usar a aproximação normal, mas no momento estou tentando usar apenas a distribuição t. Não consegui corresponder os resultados da biblioteca de estatísticas do SciPy nos meus dados de teste. Eu poderia usar um novo par de olhos para ver se estou apenas cometendo um erro estúpido em algum lugar.

Observe que isso não é tanto uma questão de codificação quanto uma "por que esse cálculo não está produzindo a estatística t correta?" Dou o código de integridade, mas não espero conselhos de software. Apenas ajude a entender por que isso não está certo.

Meu código:

import numpy as np
import scipy.stats as st

def compute_t_stat(pop1,pop2):

    num1 = pop1.shape[0]; num2 = pop2.shape[0];

    # The formula for t-stat when population variances differ.
    t_stat = (np.mean(pop1) - np.mean(pop2))/np.sqrt( np.var(pop1)/num1 + np.var(pop2)/num2 )

    # ADDED: The Welch-Satterthwaite degrees of freedom.
    df = ((np.var(pop1)/num1 + np.var(pop2)/num2)**(2.0))/(   (np.var(pop1)/num1)**(2.0)/(num1-1) +  (np.var(pop2)/num2)**(2.0)/(num2-1) ) 

    # Am I computing this wrong?
    # It should just come from the CDF like this, right?
    # The extra parameter is the degrees of freedom.

    one_tailed_p_value = 1.0 - st.t.cdf(t_stat,df)
    two_tailed_p_value = 1.0 - ( st.t.cdf(np.abs(t_stat),df) - st.t.cdf(-np.abs(t_stat),df) )    


    # Computing with SciPy's built-ins
    # My results don't match theirs.
    t_ind, p_ind = st.ttest_ind(pop1, pop2)

    return t_stat, one_tailed_p_value, two_tailed_p_value, t_ind, p_ind

Atualizar:

Depois de ler um pouco mais sobre o teste t de Welch, vi que deveria usar a fórmula Welch-Satterthwaite para calcular graus de liberdade. Eu atualizei o código acima para refletir isso.

Com os novos graus de liberdade, chego a um resultado mais próximo. Meu valor p bilateral é cerca de 0,008 da versão SciPy ... mas ainda é um erro muito grande, por isso ainda devo estar fazendo algo incorreto (ou as funções de distribuição do SciPy são muito ruins, mas é difícil de acreditar) eles são precisos apenas com 2 casas decimais).

Segunda atualização:

Enquanto continuava tentando as coisas, pensei que talvez a versão do SciPy calcule automaticamente a aproximação Normal à distribuição t quando os graus de liberdade forem altos o suficiente (aproximadamente> 30). Então, refiz meu código novamente usando a distribuição Normal, e os resultados calculados estão realmente mais distantes do SciPy's do que quando eu uso a distribuição t.

ely
fonte
Talvez SciPy calcula o teste t de Welch - a documentação do SciPy não especifica ...
Cyan
A fórmula que estou usando no meu cálculo é a mesma que a estatística t de Welch. Que eu saiba, essa é a coisa "padrão" a ser feita quando é permitido que o tamanho da amostra e as variações populacionais sejam diferentes, correto?
Ely
4
Você não precisa usar o quadrado do numerador (atual) no cálculo dos graus de liberdade? Além disso, praticamente sem alterações de código, existem maneiras muito mais seguras de calcular os valores- . A maneira como é implementada atualmente é extremamente suscetível a erros maciços devido ao cancelamento . p
cardeal
4
( 1 ) Verifique a documentação de numpy.var. A versão que vi parece indicar que a estimativa do MLE é calculada por padrão, em vez da estimativa imparcial. Para obter uma estimativa imparcial, é necessário chamá-lo com o opcional ddof=1. ( 2 ) Para obter o limite superior -valor, utilizar a simetria do -distribuição, ou seja, e ( 3 ) para a-dois atado -valor, fazer algo semelhante: . ptone_tailed_p_value = st.t.cdf(-t_stat,df)ptwo_tailed_p_value = 2*st.t.cdf(-np.abs(t_stat),df)
cardeal
2
Não acho isso tão trivial, no sentido de que muitas vezes há uma lacuna considerável entre ter uma fórmula matemática para algo em mãos e conhecer uma maneira segura e eficiente de calculá-lo. É uma daquelas coisas em que é bom ter um grande corpo de conhecimento já disponível, porque levaria uma eternidade virtual para aprender esses truques, um por um, por conta própria. :)
cardeal

Respostas:

4

Usando a função interna SciPy source (), pude ver uma impressão do código fonte da função ttest_ind (). Com base no código-fonte, o SciPy interno está executando o teste t, assumindo que as variações das duas amostras são iguais. Não está usando os graus de liberdade Welch-Satterthwaite.

Eu só quero ressaltar que, crucialmente, é por isso que você não deve apenas confiar nas funções da biblioteca. No meu caso, eu realmente preciso do teste t para populações de variações desiguais, e os graus de ajuste da liberdade podem ser importantes para alguns dos conjuntos de dados menores nos quais executarei isso. O SciPy assume variações iguais, mas não afirma essa suposição.

Como mencionei em alguns comentários, a discrepância entre meu código e o SciPy's é de cerca de 0,008 para tamanhos de amostra entre 30 e 400 e depois lentamente passa a zero para tamanhos de amostra maiores. Este é um efeito do termo extra (1 / n1 + 1 / n2) no denominador estatístico de variâncias iguais. Em termos de precisão, isso é muito importante, especialmente para amostras pequenas. Definitivamente, confirma-me que preciso escrever minha própria função. (Possivelmente, existem outras bibliotecas Python melhores, mas isso pelo menos deve ser conhecido. Francamente, é surpreendente que isso não esteja em nenhum lugar na frente e no centro da documentação do SciPy para ttest_ind ()).

ely
fonte
3
Parece que agora está implementado corretamente a partir do Scipy 0.11.0 por meio de um parâmetro opcional para especificar o teste t de Welch: docs.scipy.org/doc/scipy/reference/generated/…
Abhijit Rao