Octubre de 2016
Volumen 31, número 10
Serie de pruebas: ANOVA con C#
Por James McCaffrey
El análisis de varianza (ANOVA) es una técnica estadística clásica que se usa para deducir si todas las medias (promedios) de tres o más grupos son iguales, en situaciones donde solo tiene datos de ejemplo. Por ejemplo, supongamos que existen tres clases de introducción a la informática diferentes en la universidad. Todas las clases las imparte el mismo profesor, pero usa un libro de texto diferente y una filosofía de enseñanza distinta. Quiere averiguar si el rendimiento de los estudiantes es el mismo.
Tiene un examen que evalúa el dominio de la informática en una escala de 1 a 15, pero dado que el examen tiene un alto costo y requiere mucho tiempo, puede entregarlo solo a seis estudiantes de cada clase seleccionados de manera aleatoria. Debe administrar el examen y ejecutar ANOVA en las muestras para deducir si las medias de las tres clases son iguales o no.
Si es nuevo usuario de ANOVA, el nombre de la técnica puede ser moderadamente confuso, ya que el objetivo es analizar las medias de conjuntos de datos. ANOVA tiene este nombre porque analiza de manera oculta las varianzas para realizar deducciones sobre las medias.
Una buena manera de hacerse una idea sobre qué es ANOVA y ver el camino que va a tomar este artículo es echar un vistazo al programa de demostración de la Figura 1. La demostración configura puntuaciones codificadas para los tres grupos. Observe que solo hay cuatro puntuaciones en Group1 y solo cinco en Group3 (es bastante habitual que los tamaños de muestra sean desiguales, porque los sujetos de la prueba pueden abandonar o los datos se pueden perder o dañar).
Figura 1 Programa de demostración de ANOVA con C#
Existen dos pasos principales para ejecutar ANOVA. En el primer paso, un valor de estadística F y un par de valores denominados "grados de libertad" (df) se calculan con los datos de ejemplo. En el segundo paso, los valores de F y df se usan para determinar la probabilidad de que todas las medias de la población sean iguales (valor p). El primer paso es relativamente sencillo. El segundo paso es muy difícil.
En la demostración, el valor de F es 15.884. En general, cuanto mayor sea F, menos probable será que las medias de la población coincidan. Explicaré por qué df = (2, 12) en breve. Con los valores de F y df, se calcula que el valor p es 0.000425. Es muy bajo, por lo que se concluye que es probable que no todas las medias de la población sean iguales. En este punto, puede realizar pruebas estadísticas adicionales para determinar qué medias de la población son diferentes entre sí. Para los datos de la demostración, parece que Group1 (media de la muestra = 4.50) es peor que Group2 (media = 9.67) y Group3 (media = 10.60).
El programa de demostración
Para crear el programa de demostración, inicié Visual Studio, hice clic en Archivo | Nuevo | Proyecto y seleccioné la opción Aplicación de consola de C#. El programa de demostración no tiene ninguna dependencia significativa de .NET, por lo que funcionará con cualquier versión de Visual Studio. Después de cargar el código de plantilla en la ventana Explorador de soluciones, hice clic con el botón derecho en el archivo Program.cs, le cambié el nombre a AnovaProgram.cs y permití que Visual Studio cambiase automáticamente el nombre de la clase Program.
En la parte superior de la ventana del Editor, eliminé todos los elementos innecesarios mediante instrucciones excepto la referencia del espacio de nombres System de nivel superior. La estructura general del programa se muestra en la Figura 2. El programa de demostración es demasiado largo para presentarlo en su totalidad, pero el código fuente de la demostración completa está disponible en la descarga que se incluye con este artículo.
Figura 2 Estructura del programa de demostración
using System;
namespace Anova
{
class AnovaProgram
{
static void Main(string[] args)
{
Console.WriteLine("Begin ANOVA using C# demo");
// Set up sample data
// Use data to calculate F and df
// Use F and df to calculate p-value
Console.WriteLine("End ANOVA demo");
}
static double Fstat(double[][] data, out int[] df) { . . }
static double LogGamma(double z) { . . }
static double BetaIncCf(double a, double b, double x) { . . }
static double BetaInc(double a, double b, double x) { . . }
static double PF(double a, double b, double x) { . . }
static double QF(double a, double b, double x) { . . }
static void ShowData(double[][] data, string[] colNames) { . . }
}
}
El método static Fstat calcula y devuelve una estadística F basada en los datos almacenados en un objeto de matriz de matrices. El método también calcula y devuelve dos valores df en el parámetro out de una matriz. La función ShowData es solo una discreta función auxiliar para mostrar las medias de la muestra.
Los cinco métodos restantes se usan para calcular el valor p. El método QF es el principal. Llama al método PF, que a su vez llama al método BetaInc, que a su vez llama a los métodos BetaIncCf y LogGamma.
Después de algunos mensajes WriteLine preliminares, el método Main configura y muestra los datos de ejemplo:
double[][] data = new double[3][]; // 3 groups
data[0] = new double[] { 3, 4, 6, 5 };
data[1] = new double[] { 8, 12, 9, 11, 10, 8 };
data[2] = new double[] { 13, 9, 11, 8, 12 };
string[] colNames = new string[] { "Group1", "Group2", "Group3" };
ShowData(data, colNames);
En un escenario que no sea de demostración, sus datos se almacenarían probablemente en un archivo de texto y escribiría una función auxiliar para leer y cargar los datos en una matriz de matrices.
La estadística F y df se calculan de este modo:
int[] df = null;
double F = Fstat(data, out df);
Para ANOVA, el valor df de un conjunto de datos es un par de valores. El primer valor es K - 1, donde K es el número de grupos, y el segundo valor es N - K, donde N es el número total de valores de muestra. Por tanto, para los datos de demostración, df = (K-1, N-K) = (3-1, 15-3) = (2, 12).
El valor p se calcula y se muestra del modo siguiente:
double pValue = QF(df[0], df[1], F);
Console.Write("p-value = ");
En resumen, al ejecutar ANOVA, las instrucciones de llamada son muy simples. No obstante, hay una gran cantidad de trabajo que no es visible.
Cálculo de la estadística F
El cálculo del valor de una estadística F implica varios subpasos. Supongamos que los valores de los datos de muestra son los de la demostración:
Group1: 3.00, 4.00, 6.00, 5.00
Group2: 8.00, 12.00, 9.00, 11.00, 10.00, 8.00
Group3: 13.00, 9.00, 11.00, 8.00, 12.00
El primer subpaso consiste en calcular las medias de cada grupo y la media general de todos los valores de muestra. Para los datos de demostración:
means[0] = (3.0 + 4.0 + 6.0 + 5.0) / 4 = 4.50
means[1] = (8.0 + 12.0 + 9.0 + 11.0 + 10.0 + 8.0) / 6 = 9.67
means[2] = (13.0 + 9.0 + 11.0 + 8.0 + 12.0) / 5 = 10.60
gMean = (3.0 + 4.0 + . . . + 12.0) / 15 = 8.60
La definición del método Fstat empieza por:
static double Fstat(double[][] data, out int[] df)
{
int K = data.Length; // Number groups
int[] n = new int[K]; // Number items each group
int N = 0; // total number data points
for (int i = 0; i < K; ++i) {
n[i] = data[i].Length;
N += data[i].Length;
}
...
En este punto, la matriz local N contiene el número de valores de cada grupo, K tiene el número de grupos y N es el número total de valores de todos los grupos. A continuación, se calculan las medias de los grupos en una matriz denominada means y la media total se calcula en la variable gMean:
double[] means = new double[K];
double gMean = 0.0;
for (int i = 0; i < K; ++i) {
for (int j = 0; j < data[i].Length; ++j) {
means[i] += data[i][j];
gMean += data[i][j];
}
means[i] /= n[i];
}
gMean /= N;
El siguiente subpaso es calcular la "suma de los cuadrados entre grupos" (SSb) y la "media cuadrática entre grupos" (MSb). SSb es la suma ponderada de las diferencias de cuadrados entre cada media de grupo y la media general. MSb = SSb / (K-1), donde K es el número de grupos. Para los datos de demostración:
SSb = (4 * (4.50 - 8.60)^2) +
(6 * (9.67 - 8.60)^2) +
(5 * (10.60 - 8.60)^2) = 94.07
MSb = 94.07 / (3-1) = 47.03
El código que calcula SSb y MSb es:
double SSb = 0.0;
for (int i = 0; i < K; ++i)
SSb += n[i] * (means[i] - gMean) * (means[i] - gMean);
double MSb = SSb / (K - 1);
El siguiente subpaso es calcular la "suma de cuadrados dentro de los grupos" (SSw) y la "media cuadrática dentro de los grupos" (MSw). SSw es la suma de las diferencias de cuadrados entre cada valor de muestra y la media del grupo correspondiente. MSw = SSw / (N-K). Para los datos de demostración:
SSw = (3.0 - 4.50)^2 + . . + (8.0 - 9.67)^2 +
. . + (12.0 - 10.60)^2 = 35.53
MSw = 35.53 / (15-3) = 2.96
El código que calcula SSw y MSw es:
double SSw = 0.0;
for (int i = 0; i < K; ++i)
for (int j = 0; j < data[i].Length; ++j)
SSw += (data[i][j] - means[i]) * (data[i][j] - means[i]);
double MSw = SSw / (N - K);
El subpaso final consiste en calcular los dos valores df y la estadística F. Los dos valores df son K - 1 y N - K, y F = MSb / MSw. Para los datos de demostración:
df = (K-1, N-K) = (3-1, 15-3) = (2, 12)
F = 47.03 / 2.96 = 15.88.
El código de demostración que calcula los valores df y F es:
...
df = new int[2];
df[0] = K - 1;
df[1] = N - K;
double F = MSb / MSw;
return F;
} // Fstat
Estará de acuerdo en que calcular los valores de estadística F y df a partir de un conjunto de datos es una operación mecánica relativamente fácil una vez que se conocen las ecuaciones matemáticas.
Cálculo del valor p
La conversión de los valores de estadística F y df en un valor p que indique la probabilidad de que todas las medias de la población sean iguales según los datos de muestra que producen los valores F y df es simple en teoría, pero extremadamente difícil en la práctica. Lo explicaré lo más brevemente posible, dejando aparte la enorme cantidad de detalles que requeriría una explicación mucho más extensa. Eche un vistazo al gráfico de la Figura 3.
Figura 3 Cálculo del valor p a partir de una estadística F y valores df
Cada par de valores df posible determina un gráfico denominado distribución F. La forma de una distribución F puede variar enormemente según los valores de df. En el gráfico de la Figura 3 se muestra una distribución F para df = (4, 12). Usé df = (4, 12) en lugar del valor df = (2, 12) de los datos de demostración porque la forma de la distribución F df = (2, 12) es muy atípica.
El área total en cualquier distribución F es exactamente 1.0. Si conoce el valor de la estadística F, el valor p es el área en la distribución F de F a infinito positivo. De manera un poco confusa, el área en la distribución F de cero a la estadística F suele denominarse PF y el área en la distribución F de la estadística F a infinito positivo (que representa el valor p) suele denominarse QF. Dado que el área total en la distribución es 1, PF + QF = 1. Resulta que es un poco más fácil calcular el valor PF que el valor QF, por lo que para encontrar el valor p (QF), se suele calcular el PF y restarlo de 1 para obtener el QF.
El cálculo del valor PF es despiadadamente difícil, aunque, por suerte, conocemos ecuaciones de estimación mágicas desde hace décadas. Estas ecuaciones matemáticas, y otros cientos, se pueden encontrar en la famosa referencia "Handbook of Mathematical Functions" de M. Abramowitz e I.A. Stegun. El libro suele denominarse simplemente "A y S" entre los programadores científicos. Cada ecuación de A y S tiene un número de identificación.
En la demostración, el método PF es en realidad un contenedor alrededor del método BetaInc:
static double PF(double a, double b, double x)
{
double z = (a * x) / (a * x + b);
return BetaInc(a / 2, b / 2, z);
}
El nombre del método BetaInc significa "beta incompleta". El método BetaInc usa las ecuaciones 6.6.2 y 26.5.8 de A y S. Esas ecuaciones llaman a una función LogGamma y a una función BetaIncCf. La función LogGamma es extremadamente difícil de explicar e implementar. En resumen, la función matemática Gamma extiende la noción de factoriales a números con valoración real. Del mismo modo que los factoriales, los valores de la función Gamma pueden ser astronómicamente altos. Para controlarlos, es habitual calcular el registro de Gamma para mantener unos valores más reducidos.
El cálculo de LogGamma es muy complicado y existen varios algoritmos que puede usar. El programa de demostración usa un algoritmo denominado aproximación de Lanczos con (g=5, n=7). La referencia A y S presenta distintos algoritmos que pueden realizar el cálculo de LogGamma, pero la aproximación de Lanczos, que no se conocía cuando se publicó A y S, ofrece resultados más precisos.
El nombre del método BetaIncCf significa "beta incompleta calculada mediante fracción continua". El programa de demostración usa la ecuación 26.5.8 de A y S para el método BetaIncCf.
Resumen
Una prueba ANOVA realiza tres supuestos matemáticos: que los elementos de datos del grupo son matemáticamente independientes; que los conjuntos de datos de población se distribuyen normalmente (como en la distribución gaussiana); y que los conjuntos de datos de población tienen varianzas iguales.
Existen varias maneras de probar estas suposiciones, pero interpretar los resultados es difícil. El problema es que es muy improbable que los datos reales sean exactamente normales y tengan varianzas exactamente iguales, aunque ANOVA sigue funcionando con datos en cierto modo anormales o con varianzas diferentes. La conclusión es que es extremadamente difícil probar supuestos de ANOVA, por lo que debe ser muy cauteloso a la hora de interpretar resultados.
ANOVA guarda una estrecha relación con la prueba t. La prueba t determina si las medias de la población de exactamente dos grupos son iguales en situaciones en las que solo cuenta con datos de muestra. Por tanto, si tiene tres grupos, como en la demostración, en lugar de usar ANOVA, podría realizar tres pruebas t para comparar los grupos 1 y 2, 1 y 3, y 2 y 3. No obstante, este enfoque no se recomienda porque introduce lo que llamamos un error de tipo 1 (un falso positivo).
La variante de ANOVA que se explica en este artículo se denomina ANOVA de una vía (o un factor). Una técnica diferente, que se denomina ANOVA de dos vías, se usa cuando existen dos factores.
ANOVA se basa en el valor calculado de una estadística F de un conjunto de datos. Existen otras pruebas estadísticas que usan una estadística F. Por ejemplo, puede usar una estadística F para deducir si las varianzas de dos grupos de datos son iguales.
El Dr. James McCaffrey trabaja para Microsoft Research en Redmond, Washington. Ha colaborado en el desarrollo de varios productos de Microsoft como, por ejemplo, Internet Explorer y Bing. Puede ponerse en contacto con James en jammc@microsoft.com.
Gracias a los siguientes expertos técnicos de Microsoft por revisar este artículo: Chris Lee y Kirk Olynk