Rastreando los supuestos hechos por la función ttest_ind () de SciPy

8

Estoy tratando de escribir mi propio código Python para calcular estadísticas t y valores p para pruebas t independientes de una y dos colas. Puedo usar la aproximación normal, pero por el momento estoy tratando de usar la distribución t. No pude hacer coincidir los resultados de la biblioteca de estadísticas de SciPy con mis datos de prueba. Podría usar un par de ojos nuevos para ver si estoy cometiendo un error tonto en alguna parte.

Tenga en cuenta que no se trata tanto de una pregunta de codificación como de "¿por qué este cálculo no produce el t-stat correcto?" Doy el código para completar, pero no espero ningún consejo de software. Solo ayuda a entender por qué esto no está bien.

Mi 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

Actualizar:

Después de leer un poco más sobre la prueba t de Welch, vi que debería usar la fórmula Welch-Satterthwaite para calcular los grados de libertad. Actualicé el código anterior para reflejar esto.

Con los nuevos grados de libertad, obtengo un resultado más cercano. Mi valor p de dos lados está desactivado en aproximadamente 0.008 de la versión de SciPy ... pero esto sigue siendo un error demasiado grande, así que todavía debo estar haciendo algo incorrecto (o las funciones de distribución de SciPy son muy malas, pero es difícil de creer solo tienen una precisión de 2 decimales).

Segunda actualización:

Mientras continuaba probando cosas, pensé que tal vez la versión de SciPy calcula automáticamente la aproximación Normal a la distribución t cuando los grados de libertad son lo suficientemente altos (aproximadamente> 30). Así que volví a ejecutar mi código usando la distribución Normal, y los resultados calculados en realidad están más lejos de SciPy's que cuando uso la distribución t.

ely
fuente
Tal vez SciPy calcula la prueba t de Welch - La documentación de SciPy no especifica ...
Cyan
La fórmula que estoy usando en mi cálculo es la misma que la estadística t de Welch. Que yo sepa, esto es lo "estándar" que se debe hacer cuando los tamaños de muestra y las variaciones de población pueden ser diferentes, ¿correcto?
ely
44
¿No necesita tomar el cuadrado del numerador (actual) en el cálculo de los grados de libertad? Además, prácticamente sin cambios de código, hay formas mucho más seguras de calcular los valores . La forma en que se implementa actualmente es extremadamente susceptible a errores masivos debido a la cancelación . p
cardenal
44
( 1 ) Consulte la documentación de numpy.var. La versión que vi parece indicar que la estimación MLE se calcula por defecto en lugar de la estimación imparcial. Para obtener la estimación imparcial, hay que llamarlo con el opcional ddof=1. ( 2 ) Para la cola superior -valor, utilice la simetría de la distribución t, es decir, y ( 3 ) para la de dos colas -valor, hacer algo similar: . ptone_tailed_p_value = st.t.cdf(-t_stat,df)ptwo_tailed_p_value = 2*st.t.cdf(-np.abs(t_stat),df)
cardenal
2
No lo considero tan trivial, en el sentido de que a menudo hay una brecha considerable entre tener una fórmula matemática para algo a mano y conocer una forma segura y eficiente de calcularlo. Es una de esas cosas en las que es bueno tener una gran cantidad de conocimiento ya disponible, porque tomaría una eternidad virtual aprender tales trucos, uno por uno, todo por su cuenta. :)
cardenal

Respuestas:

4

Al utilizar la función incorporada SciPy source (), pude ver una copia impresa del código fuente de la función ttest_ind (). Basado en el código fuente, el SciPy integrado está realizando la prueba t asumiendo que las variaciones de las dos muestras son iguales. No está utilizando los grados de libertad de Welch-Satterthwaite.

Solo quiero señalar que, crucialmente, esta es la razón por la que no debe confiar solo en las funciones de la biblioteca. En mi caso, en realidad necesito la prueba t para poblaciones de variaciones desiguales, y los ajustes de grados de libertad pueden ser importantes para algunos de los conjuntos de datos más pequeños en los que ejecutaré esto. SciPy asume variaciones iguales pero no establece esta suposición.

Como mencioné en algunos comentarios, la discrepancia entre mi código y SciPy's es de aproximadamente 0.008 para tamaños de muestra entre 30 y 400, y luego va lentamente a cero para tamaños de muestra más grandes. Este es un efecto del término extra (1 / n1 + 1 / n2) en el denominador t-statistic de varianzas iguales. En cuanto a la precisión, esto es bastante importante, especialmente para muestras pequeñas. Definitivamente me confirma que necesito escribir mi propia función. (Posiblemente hay otras mejores bibliotecas de Python, pero al menos esto debería ser conocido. Francamente, es sorprendente que esto no esté en el centro de la documentación de SciPy para ttest_ind ()).

ely
fuente
3
Parece que ahora se implementa correctamente a partir de Scipy 0.11.0 a través de un parámetro opcional para especificar la prueba t de Welch: docs.scipy.org/doc/scipy/reference/generated/…
Abhijit Rao