1 #-*-coding:iso-8859-1-*-
3 # Copyright (C) 2008-2009 EDF R&D
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 Outil numerique de calcul de la variable Khi2
24 On peut realiser deux types de test du Khi2 :
25 - test d'adequation : comparer la distribution d'un echantillon a une
26 distribution theorique,
27 - test d'homogeneite : comparer les distributions de 2 vecteurs.
29 Pour le test d'adequation, on travaille sur une gaussienne
30 dont la moyenne et l'ecart type sont calcules sur
31 l'echantillon, soit donnes.
33 Ce fichier contient une classe "StatspourTests" de methodes qui realisent
34 differentes etapes utiles aux calculs des tests du Khi2.
36 Ce fichier contient de plus 3 methodes : ComputeKhi2_testGauss,
37 ComputeKhi2_Gauss et ComputeKhi2_Homogen.
38 - ComputeKhi2_testGauss : calcul la distance du Khi2 entre un vecteur
39 aleatoire issu d un gaussienne et une distribution theorique gaussienne
40 dont on specifie la moyenne et l ecart type
41 - ComputeKhi2_Gauss : calcul la distance du Khi2 entre un vecteur donne et
42 une distribution theorique gaussienne dont la moyenne et l ecart type sont
43 calcules sur l echantillon
44 - ComputeKhi2_Homogen : calcul la distance du Khi2 entre deux vecteurs donnes
46 Ces methodes necessitent et fournissent :
48 - le ou les vecteurs dont on etudie la distribution,
49 - la distribution theorique et eventuellement la moyenne et ecart type,
50 - la largeur des classes,
51 - un booleen traduisant la suppression des classes vides
53 - le vecteur des classes,
54 - les pdf theorique et donnee,
56 - la p-value qui represent l'aire de la queue de la distribution du
58 - le message qui interprete le test.
60 __author__ = "Sophie RICCI - Mars 2010"
63 from numpy import random
64 from scipy import arange, asarray, stats
65 from scipy.stats import histogram2, chisquare, chisqprob, norm
68 # ==============================================================================
69 class StatspourTests :
71 Classe de methodes pour la preparation du test de Khi2
73 def __init__(self, cdftheo=None, meantheo = None, stdtheo = None, pdftest=None,obs=None,use_mean_std_exp=True, dxmin=0.01, obsHomogen = None, nbclasses = None) :
76 if (pdftest is None and obs is None) :
77 raise ValueError('Donner soit une pdf de test soit un vecteur obs')
80 self.__obs=asarray(obs)
81 if not pdftest is None :
84 niter=eval(pdftest[2])
85 obs=[eval(" ".join(pdftest[:2])) for z in range(niter)]
86 self.__obs=asarray(obs)
88 self.__obs=asarray(eval(" ".join(pdftest[:2])))
89 if not (obsHomogen is None) :
90 self.__obsHomogen = asarray(obsHomogen)
91 self.__testHomogen = True
93 self.__testHomogen = False
96 self.__mean_exp = self.__obs.mean()
97 self.__std_exp = self.__obs.std()
99 if cdftheo is None : raiseValueError(" ... Definir le parametre cdftheo ...")
100 if use_mean_std_exp :
101 self.__cdf=cdftheo( self.__mean_exp, self.__std_exp).cdf
103 self.__cdf=cdftheo( meantheo, stdtheo).cdf
105 self.__min=min(self.__obs)
106 self.__max=max(self.__obs)
107 self.__N=len(self.__obs)
108 self.__use_mean_std_exp=use_mean_std_exp
110 self.__nbclasses = nbclasses
111 if not (dxmin is None) and not (nbclasses is None) :
112 raise ValueError("... Specifier soit le nombre de classes, soit la largeur des classes")
113 if (dxmin is None) and (nbclasses is None) :
114 raise ValueError("... Specifier soit le nombre de classes, soit la largeur des classes")
115 if not (nbclasses is None) and (dxmin is None) :
116 self.__dxmin = (self.__max - self.__min ) / float(self.__nbclasses)
119 def MakeClasses(self) :
121 Classification en classes
123 self.__subdiv=arange(self.__min,self.__max+self.__dxmin,self.__dxmin)
124 self.__modalites=len(self.__subdiv)
127 def ComputeObs(self):
129 Calcul de la probabilite observee de chaque classe
131 self.__kobs=histogram2(self.__obs,self.__subdiv)[1:]
134 def ComputeObsHomogen(self):
136 Calcul de la probabilite observee pour le test homogeneite de chaque classe
138 self.__kobsHomogen=histogram2(self.__obsHomogen,self.__subdiv)[1:]
139 return self.__kobsHomogen
141 def ComputeTheo(self):
143 Calcul de la probabilite theorique de chaque classe
145 self.__ktheo=[self.__cdf(self.__subdiv[i+1])-self.__cdf(self.__subdiv[i]) for i in range(self.__modalites-1)]
146 self.__ktheo=asarray(self.__ktheo)
147 self.__ktheo=(sum(self.__kobs)/sum(self.__ktheo))*self.__ktheo
149 def Computepdfs(self) :
151 self.__subdiv=self.__subdiv[1:]
152 self.__pdfobs=[self.__kobs[i+1]/(self.__subdiv[i+1]-self.__subdiv[i]) for i in range(self.__modalites-2)]
154 if self.__testHomogen :
155 self.__pdftheo=[self.__kobsHomogen[i+1]/(self.__subdiv[i+1]-self.__subdiv[i]) for i in range(self.__modalites-2)]
157 self.__pdftheo=[self.__ktheo[i+1]/(self.__subdiv[i+1]-self.__subdiv[i]) for i in range(self.__modalites-2)]
159 return self.__subdiv, self.__pdftheo, self.__pdfobs
161 def Computeddl(self):
163 Calcul du nombre de degres de liberte
165 self.__ddl = self.__modalites - 1.
166 if self.__use_mean_std_exp :
167 self.__ddl = self.__ddl - 2.
168 if (self.__ddl < 1.):
169 raise ValueError("The ddl is 0, you must increase the number of classes nbclasse ")
170 logging.debug("Nombre de degres de liberte=%s"%self.__ddl)
172 def ComputeValue(self) :
174 Calcul de la variable Q qui suit une loi Khi-2
176 if self.__testHomogen :
177 kobs,ktheo=self.__kobs.tolist(),self.__kobsHomogen.tolist()
179 kobs,ktheo=self.__kobs.tolist(),self.__ktheo.tolist()
181 # on supprime les classes theoriques qui ont moins d'un element (sinon la distance khi2 tendrait vers l'infini)
184 for k,val in enumerate(ktheo):
189 self.__count0 = self.__count0 +1.
190 logging.debug("WARNING : nombre de classes vides supprimees (effectif theorique inferieur a 1.) pour le calcul de la valeur du Khi2 = %s"%self.__count0)
191 ef1,ef2=asarray(ko),asarray(kt)
193 for el in ef1.tolist() :
196 for el in ef2.tolist() :
199 pourcent_nbclasse_effecinf = count /(2.*len(ef1.tolist())) *100.
200 if (pourcent_nbclasse_effecinf > 20.) :
201 logging.debug("WARNING : nombre de classes dont l effectif est inferieur a 5 elements %s"%pourcent_nbclasse_effecinf)
202 k,p = chisquare(ef1, ef2)
204 for shift in range(1,6):
205 k,p=chisquare(ef1[shift:],ef2[:-shift])
208 k,p=chisquare(ef1[:-shift],ef2[shift:])
211 logging.debug("Liste des valeurs du Khi2 = %s"%k2)
215 logging.debug("Valeur du Khi2=%s"%self.__Q)
218 def ComputeArea(self):
222 self.__areakhi2 = 100 * chisqprob(self.__Q, self.__ddl)
223 return self.__areakhi2
225 def WriteMessage(self):
227 Interpretation du test
229 message = "Il y a %.2f%s de chance de se tromper en refusant l'adequation"%(self.__areakhi2,"%")
232 def WriteMessageHomogen(self):
234 Interpretation du test
236 message = "Il y a %.2f%s de chance de se tromper en refusant l'homogeneite"%(self.__areakhi2,"%")
239 # ==============================================================================
240 def ComputeKhi2_testGauss(
246 SuppressEmptyClasses = True,
249 Test du Khi2 d adequation entre tirage aleatoire dans gaussienne et une gaussienne theo
251 essai = StatspourTests( cdftheo=norm, meantheo = meantheo, stdtheo = stdtheo, pdftest = ["random.normal","(%.3f,%.2f,%d)"%(meantheo,stdtheo,nech)], obs = None, use_mean_std_exp=False,dxmin=dx, obsHomogen = None, nbclasses = nbclasses)
255 classes,eftheo, efobs = essai.Computepdfs()
257 valeurKhi2= essai.ComputeValue()
258 areaKhi2 = essai.ComputeArea()
259 message = essai.WriteMessage()
260 logging.debug("message %s"%message)
261 return classes, eftheo, efobs, valeurKhi2, areaKhi2, message
263 def ComputeKhi2_Gauss(
266 SuppressEmptyClasses = True,
270 Test du Khi2 d adequation entre un vecteur donne et une gaussienne theo de mean et std celles du vecteur
272 essai = StatspourTests( cdftheo=norm, pdftest = None, obs = vectorV, use_mean_std_exp=True,dxmin=dx, obsHomogen = None, nbclasses = nbclasses)
276 classes,eftheo, efobs = essai.Computepdfs()
278 valeurKhi2= essai.ComputeValue()
279 areaKhi2 = essai.ComputeArea()
280 message = essai.WriteMessage()
281 logging.debug("message %s"%message)
282 return classes, eftheo, efobs, valeurKhi2, areaKhi2, message
284 def ComputeKhi2_Homogen(
288 SuppressEmptyClasses = True,
292 Test du Khi2 d homogeniete entre 2 vecteurs
294 essai = StatspourTests( cdftheo=norm, pdftest = None, obs = vectorV1, use_mean_std_exp=True,dxmin=dx, obsHomogen = vectorV2, nbclasses = nbclasses)
297 essai.ComputeObsHomogen()
298 classes,eftheo, efobs = essai.Computepdfs()
300 valeurKhi2= essai.ComputeValue()
301 areaKhi2 = essai.ComputeArea()
302 message = essai.WriteMessageHomogen()
303 logging.debug("message %s"%message)
304 return classes, eftheo, efobs, valeurKhi2, areaKhi2, message
306 # ==============================================================================
307 if __name__ == "__main__":
308 print '\n AUTODIAGNOSTIC \n'
310 numpy.random.seed(100)
312 # Test de verification d adequation entre une gaussienne et un tirage gaussien
314 print 'Test de verification d adequation entre une gaussienne centree normale et un tirage gaussien'
315 classes, eftheo, efobs, valeurKhi2, areaKhi2, message = ComputeKhi2_testGauss(meantheo = 0., stdtheo = 1., nech = 1000., dx = 0.1, SuppressEmptyClasses = True, nbclasses = None)
316 print ' valeurKhi2=',valeurKhi2
317 print ' areaKhi2=',areaKhi2
320 if (numpy.abs(areaKhi2 - 99.91)< 1.e-2) :
321 print "The computation of the khisquare value is OK"
323 raise ValueError("The computation of the khisquare value is WRONG")
325 numpy.random.seed(2490)
327 # Test de verification d adequation entre une gaussienne et un vecteur donne
329 print 'Test de verification d adequation entre une gaussienne et un vecteur donne'
330 V = random.normal(50.,1.5,1000)
331 classes, eftheo, efobs, valeurKhi2, areaKhi2, message = ComputeKhi2_Gauss(dx = 0.1, vectorV = V, SuppressEmptyClasses = True, nbclasses = None)
332 print ' valeurKhi2=',valeurKhi2
333 print ' areaKhi2=',areaKhi2
336 if (numpy.abs(areaKhi2 - 99.60)< 1.e-2) :
337 print "The computation of the khisquare value is OK"
339 raise ValueError("The computation of the khisquare value is WRONG")
341 # Test de d homogeneite entre 2 vecteurs donnes
343 print 'Test d homogeneite entre 2 vecteurs donnes'
344 V1 = random.normal(50.,1.5,10000)
345 numpy.random.seed(2490)
346 V2 = random.normal(50.,1.5,10000)
347 classes, eftheo, efobs, valeurKhi2, areaKhi2, message = ComputeKhi2_Homogen(dx = 0.5, vectorV1 = V1, vectorV2 = V2, SuppressEmptyClasses = True, nbclasses = None)
348 print ' valeurKhi2=',valeurKhi2
349 print ' areaKhi2=',areaKhi2
352 if (numpy.abs(areaKhi2 - 99.98)< 1.e-2) :
353 print "The computation of the khisquare value is OK"
355 raise ValueError("The computation of the khisquare value is WRONG")
357 # Test de verification d adequation entre une gaussienne et un tirage gaussien en faisant varier le nombre de classes, echantillon de taille 10000
359 print 'Test de verification d adequation entre une gaussienne et un vecteur aleatoire gaussien de taille 10000'
360 # file = 'ComputeKhi2_adequationGauss_fctnbclasses_nech10000.gnu'
361 # fid = open(file, "w")
362 # lines = '%s\n' % ('# dx , nbclasses, valeurKhi2, ProbKhi2' )
363 numpy.random.seed(4000)
364 V = random.normal(0., 1.,10000)
366 for dx in arange(0.01, 1., 0.001) :
367 classes, eftheo, efobs, valeurKhi2, areaKhi2, message = ComputeKhi2_Gauss(dx = dx, vectorV = V, SuppressEmptyClasses = True, nbclasses = None)
368 # lines += '%f %f %f %f\n' % (dx, numpy.size(classes), valeurKhi2, areaKhi2)
369 aire.append(areaKhi2)
370 meanaire = numpy.asarray(aire)
371 # fid.writelines(lines)
373 print " En moyenne, il y a ", meanaire.mean(),"% de chance de se tromper en refusant l adequation a une loi gaussienne pour un echantillon de taille 10000"
375 if (numpy.abs( meanaire.mean() - 71.79)< 1.e-2) :
376 print "The computation of the khisquare value is OK"
378 raise ValueError("The computation of the khisquare value is WRONG")
380 # Test de verification d adequation entre une gaussienne et un tirage gaussien en faisant varier le nombre de classes, echantillon de taille 1000
382 print 'Test de verification d adequation entre une gaussienne et un vecteur aleatoire gaussien de taille 1000'
383 # file = 'ComputeKhi2_adequationGauss_fctnbclasses_nech1000.gnu'
384 # fid = open(file, "w")
385 # lines = '%s\n' % ('# dx , nbclasses, valeurKhi2, ProbKhi2' )
386 numpy.random.seed(4000)
387 V = random.normal(0., 1.,1000)
389 for dx in arange(0.05, 1., 0.001) :
390 classes, eftheo, efobs, valeurKhi2, areaKhi2, message = ComputeKhi2_Gauss(dx = dx, vectorV = V, SuppressEmptyClasses = True, nbclasses = None)
391 # lines += '%f %f %f %f\n' % (dx, numpy.size(classes), valeurKhi2, areaKhi2)
392 aire.append(areaKhi2)
393 meanaire = numpy.asarray(aire)
394 # fid.writelines(lines)
396 print " En moyenne, il y a ", meanaire.mean(),"% de chance de se tromper en refusant l adequation a une loi gaussienne pour un echantillon de taille 1000"
398 if (numpy.abs( meanaire.mean() - 90.60)< 1.e-2) :
399 print "The computation of the khisquare value is OK"
401 raise ValueError("The computation of the khisquare value is WRONG")
403 # Test de verification d adequation entre une gaussienne et un tirage gaussien en faisant varier le nombre de classes, echantillon de taille 100
405 print 'Test de verification d adequation entre une gaussienne et un vecteur aleatoire gaussien de taille 100'
406 # file = 'ComputeKhi2_adequationGauss_fctnbclasses_nech100.gnu'
407 # fid = open(file, "w")
408 # lines = '%s\n' % ('# dx , nbclasses, valeurKhi2, ProbKhi2' )
409 numpy.random.seed(4000)
410 V = random.normal(0., 1.,100)
412 for dx in arange(0.1, 1., 0.01) :
413 classes, eftheo, efobs, valeurKhi2, areaKhi2, message = ComputeKhi2_Gauss(dx = dx, vectorV = V, SuppressEmptyClasses = True, nbclasses = None)
414 # lines += '%f %f %f %f\n' % (dx, numpy.size(classes), valeurKhi2, areaKhi2)
415 aire.append(areaKhi2)
416 meanaire = numpy.asarray(aire)
417 # fid.writelines(lines)
419 print " En moyenne, il y a ", meanaire.mean(),"% de chance de se tromper en refusant l adequation a une loi gaussienne pour un echantillon de taille 100"