Data science : Les tests de Kolmogorov-Smirnov et Lilliefors

Les tests de Kolmogrov-Smirnov(KS) et Lilliefors(LF) sont certainement les tests les plus populaires lorsqu’il s’agit de tester la normalité d’un échantillon. Comme nous l’avons vu dans le premier article Data science : Droite de Henry, ces tests ne sont pas seulement réservés à la loi normale, la normalité n’étant qu’un cas particulier. Pour être plus précis, ils permettent de tester si un échantillon provient d’une distribution continue. Le test de Lilliefors, en particulier, ne s’applique qu’à trois distributions continues : NormaleExponentielle et Uniforme.

La différence principale entre ces deux tests est qu’on utilise le test de KS lorsque les paramètres de la distribution sont connus, et le test de LF lorsqu’ils ne sont pas connus mais estimés grâce à l’échantillon.

Ces tests sont implémentés sous scipy (kstest) et openturns(Fittingtests.Kolmogrov, Fittingtests.Lilliefors) ou encore statsmodels(statsmodels.stats.diagnostic.lilliefors).

Un peu de théorie

Supposons que nous ayons une série de n mesures x_i telle que :

    \[$x_{i-1} \leq x_i$ et $1 \leq i \leq n$\]

Test de KS

On a recourt au test de KS lorsqu’on veut vérifier si la série de mesure proviendrait d’une certaine distribution continue, entièrement déterminée. Sa fonction de répartition est appelée fonction hypothèse. En particulier, il peut s’agir d’une loi normale, qui est entièrement déterminée par ses deux paramètres : sa moyenne \mu et son écart-type \sigma. C’est ce qui nous intéresse.
Soient F la fonction de répartition d’une loi normale (la fonction hypothèse) et F_n la fonction de répartition empirique de nos données x_i, i =1,...,n. La statistique de test est définie par :

    \[T = \sup_x{|F(x) - F_n(x)|}\]

Les hypothèses du test sont : 

  • H0 : F_n(x) = F(x) pour tout x (Nos données suivent une loi normale)
  • H1 : F_n(x) \neq F(x) pour au moins un x donné (nos données ne suivent pas une loi normale)

Test de LF

Ce test est une modification du KS test. Ici on suppose que les moments de la distribution sont inconnus. Il faut donc les estimer à partir des données.
Soient S_n la fonction de répartition des données et F celle d’une variable X suivant une loi normale avec comme moyenne \mu (moyenne empirique estimée sur les données) et comme écart-type \sigma (écart-type corrigé estimé sur les données). La statistique de test du LF test est définie par :

    \[D = \max{|F(x) - S_n(x)|}\]

.
Les hypothèses du test sont exactement les mêmes que pour le test KS.

En pratique

Exemple 1

Considérons un échantillon de taille N = 1000 provenant d’une loi normale.

En utilisant scipy et kstest, on a :

from scipy import stats

stats.kstest(stats.norm.rvs, 'norm', N=100)
KstestResult(statistic=0.07438290129902819, pvalue=0.6107078771344361)

Sans surprise la p-value est d’environ 0.6 et est bien plus élevée que le seuil de 0.05 fixé par défaut dans scipy. On ne peut pas rejeter H0 au risque de 5 \%.

Essayons maintenant avec statsmodels et lilliefors :

from statsmodels.stats.diagnostic import lilliefors
import scipy.stats as stats

n = 1000
mu = 2
sigma = 1.2
data = stats.norm.rvs(loc=mu, scale=sigma, size=n)

statistic, p_value = lilliefors(data, 'norm')
print("statistic =", statistic, '\n',
      "p_value =", p_value)
statistic = 0.015392333058231733 
p_value = 0.8747119829450105

La p_value est d’environ 0.87 donc logiquement plus grande que le seuil fixé par défaut 0.05. On ne rejette pas H0 avec un risque de 5 \%.

Exemple 2

Considérons un échantillon provenant d’une loi normale standard. Nous utiliserons les options Fittingtests.Kolmogrov et Fittingtests.Lilliefors de openturns.

Avec Fittingtests.Kolmogrov :

import openturns as ot

n = 1000
distribution = ot.Normal()
data = distribution.getSample(n)
threshold = 0.05

test = ot.FittingTest.Kolmogorov(data, distribution, threshold)

print('Conclusion =', test.getBinaryQualityMeasure(), '\n',
      'P-value =', test.getPValue())
Conclusion = True 
P-value = 0.5758608108872229

La méthode getBinaryQualityMeasure() compare automatiquement la p-value au seuil fixé et nous permet de conclure directement sur la normalité de l’échantillon. Bien entendu, le résultat du test nous indique que l’échantillon provient d’une distribution normale.

Avec Fittingtests.Lilliefors :

import openturns as ot

n = 1000
distributionFactory = ot.NormalFactory()
data = distribution.getSample(n)
threshold = 0.05

dist, test = ot.FittingTest.Lilliefors(data, distributionFactory, threshold)
print('Conclusion=', test.getBinaryQualityMeasure(), '\n',
      'P-value=', test.getPValue(), '\n',
      'dist_and_param =', dist)
Conclusion= True 
P-value= 0.8523809523809524 
dist_and_param = Normal(mu = -0.0315476, sigma = 0.998111)

Comme précédemment, la méthode getBinaryQualityMeasure() nous permet de conclure directement sur la normalité de l’échantillon. Mais ici on peut aussi récupérer la distribution avec les paramètres estimés sur les données, qu’on affiche avec la variable dist_and_param.

C’est la fin de cet article dans lequel nous avons parcouru les tests de Kolgomorov-Smirnov et de Lilliefors. Nous verrons dans un prochain article le test de Shapiro-Wilk.