Partager via



Mars 2017

Volume 32, numéro 3

Cet article a fait l'objet d'une traduction automatique.

Série de tests - Ajustement du Khi-deux en C#

Par James McCaffrey

James McCaffreyUne adéquation (également appelée khi) du Khi-deux de test adapté est une technique statistique courante qui est utilisée pour déterminer si les données de nombre observé correspondent aux données de nombre attendu. Par exemple, supposons que vous disposez de trois serveurs Web conçues pour gérer de 50 %, 30 % et 20 % de votre trafic, respectivement. Si vous observez de 1 000 demandes HTTP, vous vous attendez à voir 500 demandes traitées par le premier serveur, 300 demandes traitées par le second serveur et 200 requêtes traitées par le troisième serveur.

Mais supposons que votre nombre observée réelles est (480, 290, 230). Vous décidez que les différences entre les nombres observées et attendues sont simplement en raison de caractère aléatoire, ou vous conclure preuve statistique que vos serveurs Web ne sont pas gérer le trafic comme prévu ? Voici un exemple du type de problème que peut répondre à une adéquation du Khi-deux de test adapté.

Une bonne façon de voir où cet article est menée consiste à examiner le programme de démonstration dans Figure 1. Le programme de démonstration analyse une roue de roulette de style américain. Il s’agit d’une adéquation du Khi-deux standard d’exemple adaptée. Une roue American roulette a 38 emplacements, où 18 sont rouges, 18 sont noirs et 2 sont verts. Par conséquent, si vous faire tourner la roulette de 380 heures et la roulette est juste en ce sens que chaque emplacement est tout aussi probable, vous vous attendez à voir 180 rouges, 180 noir et 20 verts.

Adéquation du Khi-deux de démonstration adaptée
Figure 1 adéquation du Khi-deux de démonstration adaptée

Mais supposons que vous observé nombres de (192, 163, 25) — quelques rouges plus et verts que prévu et noirs moins que prévu. Le programme de démonstration utilise les nombres observées et prévues pour calculer une statistique du Khi-deux de 3,66. Cette valeur est une mesure du différents les nombres observées et attendues, où les valeurs élevées indiquent une différence supérieure.

La démonstration utilise ensuite la statistique du Khi-deux calculée pour calculer une valeur p (« valeur de probabilité ») de 0.1608. Cette valeur est la probabilité approximative, que vous voyez une valeur calculée du Khi-deux 3,66 égale ou supérieure si la roulette de la roulette agit juste. Autrement dit, est la valeur p petit, plus la roulette n’est pas juste. Ici, la valeur de 0.1608 p est non concluante. La probabilité que la roue est juste est 0.1608, par conséquent, la probabilité que la roue n’est pas juste est 1 - 0.1608 = 0.8392 est assez élevé, mais pas déterminante.

Cet article suppose que vous disposez d’intermédiaire ou compétences en programmation plus mais ne suppose pas que vous connaissez un peu adéquation du Khi-deux tests adaptée. Le programme de démonstration est codé avec c#, mais vous ne devez avoir aucun mal à refactoriser le code dans un autre langage, tel que JavaScript ou Python. Le code de démonstration est présenté dans cet article et est également disponible dans le téléchargement qui accompagne cet article.

La Structure de programme de démonstration

La structure générale du programme de démonstration, avec quelques modifications mineures à économiser de l’espace, est présentée dans Figure 2. Utiliser un style de la méthode statique plutôt que dans un style de programmation orientée objet pour plus de simplicité. La méthode Main est la logique de contrôle. Le programme de démonstration n’est pas tout à fait aussi compliqué qu’il peut apparaître tout d’abord, car la plupart des neuf méthodes est courtes.

Figure 2 Structure de programme de démonstration du Khi-deux

using System;
namespace ChiSquaredUsingCSharp
{
  class ChiSquaredProgram
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Begin demo \n");
      // 1. Calculate chi-squared stat.
      // 2. Use chi-squared to calculate p-value.
      Console.WriteLine("End demo");
      Console.ReadLine();
    }
    public static void ShowVector(int[] v) { . . }
    public static void ShowVector(double[] v,
      int dec) { . . }
    public static double ChiFromFreqs(int[] observed,
      double[] expected) { . . }
    public static double ChiFromProbs(int[] observed,
      double[] probs) { . . }
    public static double[] ExpectedFromProbs(
      double[] probs, int N) { . . }
    public static double ChiSquarePval(double x,
      int df) { . . }
    private static double Exp(double x) { . . }
    public static double Gauss(double z) { . . }
  } // Program
} // ns

Pour créer le programme de démonstration, j’ai lancé Visual Studio a créé un programme d’application de console c# et nommé ChiSquaredUsingCSharp. J’ai utilisé Visual Studio 2015, mais le programme de démonstration n’a aucune dépendance significative de .NET pour n’importe quelle version récente de Visual Studio fonctionne.

Après que le code du modèle chargé dans la fenêtre de l’éditeur, j’ai cliqué sur le fichier Program.cs dans la fenêtre Explorateur de solutions renommé le fichier ChiSquaredProgram.cs, puis autorisé Visual Studio renommer automatiquement la classe Program pour moi. En haut du code généré par modèle, j’ai supprimé tout inutiles à l’aide d’instructions, en laissant le seul qui fait référence à l’espace de noms système supérieur.

La méthode principal configure les nombres observés la roulette de la roulette de la façon suivante :

Console.WriteLine("Is roulette wheel fair");
Console.WriteLine("Wheel was spun 380 times");
int[] observed = new int[] { 192, 163, 25 };  // 380
Console.WriteLine("Observed counts red, black, green:");
ShowVector(observed);

Je viens d’inventer ces nombres observés car ils vous fournissent un exemple représentatif. Méthode d’assistance ShowVector qui affiche un tableau d’entiers est défini :

public static void ShowVector(int[] v)  {
  for (int I = 0; i < v.Length; ++i)
    Console.Write(v[i] +“" “");
  Console.WriteLine“"\”");
}

Ensuite, au lieu de configurer directement le nombre attendu, je configure les indirectement comme suit :

double[] probs = new double[] {
  18.0/38, 18.0/38, 2.0/38 };
Console.WriteLine("Probabilities if fair:");
ShowVector(probs, 4);
double[] expected = ExpectedFromProbs(probs, 380);
Console.WriteLine("Expected counts if fair:");
ShowVector(expected, 1);

Les probabilités de rouge, noir et vert sont 18/38, 18/38 et 2/38, comme expliqué précédemment. Méthode d’assistance ExpectedFromProbs accepte un tableau de probabilités et le nombre total et retourne un tableau du nombre attendu. J’aurais pu directement configurer le nombre attendu pour 380 procède de la roulette de la roulette comme suit :

double[] expected = new double[] { 180.0, 180.0, 20.0 };

La méthode d’assistance du nombre attendu est définie :

public static double[] ExpectedFromProbs(double[] probs,
  int N)
{
  double[] expected = new double[probs.Length];
  for (int i = 0; i < probs.Length; ++i)
    expected[i] = probs[i] * N;
  return expected;
}

Et la méthode surchargée ShowVector pour un tableau de type double est définie :

public static void ShowVector(double[] v, int dec) {
  for (int i = 0; i < v.Length; ++i)
    Console.Write(v[i].ToString("F" + dec) + "  ");
  Console.WriteLine("\n");
}

Le programme de démonstration se poursuit en calculant la statistique du Khi-deux :

double chi = ChiFromProbs(observed, probs); 
Console.WriteLine("Calculated chi-squared = " +
  chi.ToString("F2"));  // 3.66

Méthode ChiFromProbs utilise une signature qui accepte un tableau d’entiers de nombres observées et un tableau de type double de probabilités attendus, essentiellement car il s’agit de la signature utilisée par la fonction de chisq.test du langage R équivalente. Par exemple, dans un environnement R interactif, vous pouvez effectuer la démonstration comme suit :

> obs <- c(192, 163, 25)
> probs <- c(18/38, 18/38, 2/38)
> chisq.test(x=obs, p=probs)
X-squared = 3.6556, df = 2, p-value = 0.1608

Notez que les statistiques du Khi-deux calculée (3,66) et la valeur p (0.1608) obtenue à l’aide de R sont les mêmes valeurs calculées par le programme de démonstration. La démonstration se termine à l’aide de la statistique du Khi-deux calculée pour calculer la valeur p :

int df = observed.Length - 1;
double pval = ChiSquarePval(chi, 2);
Console.WriteLine("The pval with df of " + df +
  " = " + pval.ToString("F4") );
Console.WriteLine("Pval is probability, if wheel fair,");
Console.WriteLine("you'd see a chi-squared as calculated");
Console.WriteLine("End demo");
Console.ReadLine();

Df variable signifie degrés de liberté, je reviendrai bientôt.

Présentation de la statistique du Khi-deux

Si vous avez un tableau de nombres observées et un tableau de nombres attendus, vous pouvez calculer une mesure appelée du Khi-deux, sont une mesure des différents nombres observées et prévues. Valeurs élevées indiquent une différence supérieure.

La statistique du Khi-deux est définie comme la somme des différences au carré entre observées et attendues divisé par prévu :

chi-squared = sum( (obs[i] - exp[i])^2 / exp[i] )

L’idée est mieux expliquer par un exemple. Supposons que, comme dans la démonstration, les nombres observées pour 380 spins d’une roue de roulette sont (192, 163, 25) et le nombre attendu si la roulette est juste (180, 180, 20). La statistique du Khi-deux calculée est la suivante :

chi-squared = (192 - 180)^2 / 180 +
              (163 - 180)^2 / 180 +
              (25 - 20)^2   / 20
            = (144 / 180) + (289 / 180) + (25 / 20)
            = 0.8000 + 1.6056 + 1.2500
            = 3.6556

La démonstration met en œuvre cette fonction en tant que :

public static double ChiFromFreqs(int[] observed,
  double[] expected)
{
  double sum = 0.0;
  for (int i = 0; i < observed.Length; ++i) {
    sum += ((observed[i] - expected[i]) *
      (observed[i] - expected[i])) / expected[i];
  }
  return sum;
}

Il n’existe aucune vérification des erreurs, par souci de simplicité, mais dans un système de production, vous souhaiterez vous assurer que les tableaux observées et prévues ont la même longueur et ainsi de suite. Le programme de démonstration possède également une méthode pour calculer une statistique du Khi-deux, à partir d’un tableau de nombres observées et un tableau de probabilités attendus :

public static double ChiFromProbs(int[] observed,
  double[] probs)
{
  int n = observed.Length;
  int sumObs = 0;
  for (int i = 0; i < n; ++i)
    sumObs += observed[i];
  double[] expected = ExpectedFromProbs(probs, sumObs);
  return ChiFromFreqs(observed, expected);
}

Pour résumer, la définition mathématique de la statistique du Khi-deux utilise un tableau de nombres observées et un tableau de nombres attendus. Il est également intéressant de calculer du Khi-deux d’un tableau de nombres observées et un tableau de probabilités attendus. Pour effectuer ce calcul, il est utile de disposer d’une méthode d’assistance qui calcule le nombre attendu de probabilités attendues.

Présentation de la Distribution du Khi-deux

Si vous avez une valeur calculée Khi-deux, vous pouvez l’utiliser pour calculer la probabilité d’obtenir cette valeur du Khi-deux (valeur p). Cette idée est expliquée plus visuellement. Examinez le graphique de Figure 3, qui montre la distribution du Khi-deux pour le problème de démonstration. La zone totale sous n’importe quelle distribution du Khi-deux est 1.0. La valeur de p est la zone sous le graphique à partir du Khi-deux calculée à + infini.

La Distribution du Khi-deux pour df = 2
Figure 3 la Distribution du Khi-deux pour df = 2

Cette idée est très subtile. Et, malheureusement, il est très difficile de calculer la zone sous une distribution du Khi-deux. Heureusement, les algorithmes numériques pour estimer la zone sous une distribution du Khi-deux ont été développés dans les années 1960 et 1970 et sont toujours utilisés aujourd'hui.

Compliquer les choses est le fait qu’il n’est pas simplement une distribution du Khi-deux, il existe de nombreux. Bref, pour une qualité de test adapté, il existe une valeur appelée les degrés de liberté, df abrégé. La valeur de df pour une adéquation du test adapté est simplement 1 moins le nombre de compartiments de nombre. Il existe trois compartiments count (rouge, noir, vert) pour la démonstration, donc le df est 3-1 = 2.

Il existe une distribution du Khi-deux différente pour chaque valeur de df. Graphique de Figure 3 affiche la distribution du Khi-deux pour df = 2. Notez les valeurs sur l’axe horizontal s’exécutent à 0.0 (le plus petit possible calculée Khi-deux, lorsque tous les observés nombre égal à leurs décomptes attendus correspondants), + infini (il existe qu'aucune limite aux différents compteurs observées et prévues ne peut être) du Khi-deux.

Il existe plusieurs algorithmes sophistiqués qui peuvent calculer une valeur de p / zone sous le graphique de la distribution du Khi-deux. Le programme de démonstration utilise ce qu’on appelle ACM algorithme 299, qui est implémentée en tant que méthode ChiSquarePval. L’algorithme utilise à son tour un autre algorithme appelé 209 ACM, qui est implémentée en tant que méthode de Gauss. Ces algorithmes sont des notions de base sur des calculs numériques et sont présentées dans Figure 4. Même un bref coup de œil au code devrait vous convaincre d’il existe quelques calculs très grave passe, mais heureusement, vous pouvez considérer les méthodes qui implémentent ces algorithmes comme boîtes noires car vous n’aurez jamais à modifier le code.

Figure 4 méthodes ChiSquarePval et Gauss

public static double ChiSquarePval(double x, int df)
{
  // x = a computed chi-square value.
  // df = degrees of freedom.
  // output = prob. x value occurred by chance.
  // ACM 299.
  if (x <= 0.0 || df < 1)
    throw new Exception("Bad arg in ChiSquarePval()");
  double a = 0.0; // 299 variable names
  double y = 0.0;
  double s = 0.0;
  double z = 0.0;
  double ee = 0.0; // change from e
  double c;
  bool even; // Is df even?
  a = 0.5 * x;
  if (df % 2 == 0) even = true; else even = false;
  if (df > 1) y = Exp(-a); // ACM update remark (4)
  if (even == true) s = y;
  else s = 2.0 * Gauss(-Math.Sqrt(x));
  if (df > 2)
  {
    x = 0.5 * (df - 1.0);
    if (even == true) z = 1.0; else z = 0.5;
    if (a > 40.0) // ACM remark (5)
    {
      if (even == true) ee = 0.0;
      else ee = 0.5723649429247000870717135;
      c = Math.Log(a); // log base e
      while (z <= x) {
        ee = Math.Log(z) + ee;
        s = s + Exp(c * z - a - ee); // ACM update remark (6)
        z = z + 1.0;
      }
      return s;
    } // a > 40.0
    else
    {
      if (even == true) ee = 1.0;
      else
        ee = 0.5641895835477562869480795 / Math.Sqrt(a);
      c = 0.0;
      while (z <= x) {
        ee = ee * (a / z); // ACM update remark (7)
        c = c + ee;
        z = z + 1.0;
      }
      return c * y + s;
    }
  } // df > 2
  else {
    return s;
  }
} // ChiSquarePval()
private static double Exp(double x)
{
  if (x < -40.0) // ACM update remark (8)
    return 0.0;
  else
    return Math.Exp(x);
}
public static double Gauss(double z)
{
  // input = z-value (-inf to +inf)
  // output = p under Normal curve from -inf to z
  // ACM Algorithm #209
  double y; // 209 scratch variable
  double p; // result. called ‘z’ in 209
  double w; // 209 scratch variable
  if (z == 0.0)
    p = 0.0;
  else
  {
    y = Math.Abs(z) / 2;
    if (y >= 3.0)
    {
      p = 1.0;
    }
    else if (y < 1.0)
    {
      w = y * y;
      p = ((((((((0.000124818987 * w
        - 0.001075204047) * w + 0.005198775019) * w
        - 0.019198292004) * w + 0.059054035642) * w
        - 0.151968751364) * w + 0.319152932694) * w
        - 0.531923007300) * w + 0.797884560593) * y
        * 2.0;
    }
    else
    {
      y = y - 2.0;
      p = (((((((((((((-0.000045255659 * y
        + 0.000152529290) * y - 0.000019538132) * y
        - 0.000676904986) * y + 0.001390604284) * y
        - 0.000794620820) * y - 0.002034254874) * y
       + 0.006549791214) * y - 0.010557625006) * y
       + 0.011630447319) * y - 0.009279453341) * y
       + 0.005353579108) * y - 0.002141268741) * y
       + 0.000535310849) * y + 0.999936657524;
    }
  }
  if (z > 0.0)
    return (p + 1.0) / 2;
  else
    return (1.0 - p) / 2;
} // Gauss()

Pour résumer

Il est improbable que vous aurez besoin analyser les données de roue de roulette, mais une fois que vous avez compris le type de problème du scénario où une adéquation du Khi-deux de test adapté est applicable, vous pouvez trouver de nombreuses utilisations pratiques pour le test. Il existe de nombreux outils qui peuvent effectuer une adéquation du Khi-deux de test adapté, y compris Excel et le langage R, mais ces outils peuvent être difficiles ou impossibles à intégrer dans un système logiciel. Dans ce cas, vous pouvez utiliser le code présenté dans cet article.

Il existe plusieurs autres types de tests statistiques qui utilisent la distribution du Khi-deux. Les tests présentés dans cet article sont parfois appelé test de Khi-deux de Pearson pour les distinguer d’autres tests du Khi-deux. En outre, il existe plusieurs autres tests statistiques qui peut comparer les nombres observées et attendues, bien que du Khi-deux est le plus courant.

Il est important de se rappeler que l’adéquation du Khi-deux de test adapté, comme la plupart des tests statistiques, est PROBABILISTE, donc vous devez interpréter les résultats plus restrictives. Même si vous obtenez une petite valeur p, il est préférable de dire quelque chose comme « la petite valeur p de 0.0085 suggère que la différence entre les nombres observées et prévues ne se termine par hasard », plutôt que, « la petite valeur p indique les nombres observées n’a pas pu se sont produites par hasard ».


Récupération d’urgence. James McCaffreytravaille pour Microsoft Research à Redmond, Wash.  Il a travaillé sur plusieurs produits Microsoft, y compris Internet Explorer et Bing. Dr. James McCaffrey peut être atteint à jammc@microsoft.com.

Je remercie les experts techniques Microsoft suivants qui cet article : Chris Lee et Kirk Olynyk