/**************************************************************************** Segmentation automatique des séries statistiques ordonnées (janvier 2004) Gaetan Peaquin - gaetan.peaquin@free.fr Cyril Labbe - cyril.labbe@imag.fr Dominique Labbe - dominique.labbe@iep.upmf-grenoble.fr D'après : Pierre HUBERT, Jean-Pierre CARBONNEL et Ali CHAOUCHE, "Segmentation des séries hydrométéorologiques", Journal of hydrology, 110, 1989,349-367. Et Pierre HUBERT, Cyril LABBE et Dominique LABBE, "Segmentation automatique des corpus", in Annie MORIN et Pascale SEBILLOT (eds), VIe Journées d'Analyse des Données Textuelles,Rennes, IRISA-INRIA, 2002, vol 1, 359-369. ****************************************************************************/ /* Programme de segmentation d'une série ordonnée */ #include #include #include #include #include "liste.h" #include "option.h" #include "dcdflib.h" #include "segmenter.h" /* Fonction main */ /* * Le programme recherche la segmentation optimale, * i.e. réduisant l'écart quadratique à la moyenne * sur chaque segment, d'une série ordonnée * return 0 si tout s'est bien déroulé */ int main(int argc, char *argv[]) { /* indice quelconque */ int a; /* la validité des segmentations produites sur la série complète */ int valide = 1; /* tampon */ float tmp; // Initialisation if (InitVar(argc,argv) != 0) { printf("ERROR : La phase d'initialisation a echoué\n"); return 1; } if (InitTab(n) != 0) { printf("ERROR : La phase d'initialisation des structures internes a echoué\n"); return 1; } k = 1; while ( (k<=ksup) && valide && (Dmin != 0) ) //pour tous les ordres de segmentation à considérer //tant que la segmentation est valide //et que le minimum n'est pas atteint { i = k; while (i <= n) //pour toutes les sous-séries possibles { D[k][i] = CalculD(k,i); i++; } valide = EstValideSeg(&tlseg[k][i-1],alpha); if ( (D[k][n] < Dmin) && valide ) { Dmin = D[k][n]; if(verb) { if (!seuil_variable) { printf("Segmentation d'ordre %i",k); Afficherlseg(&tlseg[k][n]); printf("\n\n"); } else if ((seuil_variable) && (k >1)) { printf("Segmentation d'ordre %i",k); if (max_alpha[k] == 1) printf(", non acceptée\n"); else printf(", acceptée\n"); a = 1; tmp = 0.0; printf("Tableau des indices de confiance : { %.2f",(1.0 - talpha[k][a]) * 100); tmp = tmp + ((1.0 - talpha[k][a]) * 100); for (a=2;a 1 { if (k == i) { Dopt = 0.0; for (z=1;z<=i;z++) { seg = CreerSegment(1,NULL); InsererTete(lsegopt,seg); } } else { Dopt = MAXFLOAT; l = 1; // longueur du dernier segment while ( (l <= i-1) && !(stop) ) { if (d[n-i+1][n-i+l] == -1.0) { d[n-i+1][n-i+l] = Calculd(n-i+1,n-i+l); } if (d[n-i+1][n-i+l] < Dmin) { Dtmp = d[n-i+1][n-i+l] + D[k-1][i-l]; if (Dtmp < Dmin) { if (Dtmp < Dopt) { if ( (l >= tmin) && (n-l >= tmin) ) // pour assurer une longueur minimale de segment { Dopt = Dtmp; *lsegopt = tlseg[k-1][i-l]; seg = CreerSegment(l,NULL); InsererTete(lsegopt,seg); } } } } else { // il n'est plus nécessaire de continuer // à augmenter la longueur du dernier segment stop = 1; } l++; } } } tlseg[k][i] = (*lsegopt); return Dopt; } /// Calcul de l'écart quadratique sur un segment float Calculd(int e, int f) { // indice quelconque int z; // pour le calcul local de la moyenne float moyn = 0.0; // pour le calcul local de d float d = 0.0; // Calcul de la moyenne sur le segment [e;f] for(z=e;z<=f;z++) { moyn = moyn + serie[z]; } moyn = (moyn / (f-e+1)); moy[e][f] = moyn; // Calcul de l'écart quadratique sur le segment [e;f] for(z=e;z<=f;z++) { d = ( d + pow((serie[z] - moy[e][f]),2) ); } return d; } /// Initialisation du tableau d[][] int InitTab(int b) { // indices quelconques int y,z; if (b <= 0) { printf("ERROR : Taille de tableau interne erronée\n"); return 1; } for (y=0;y<=b;y++) { for (z=y;z<=b;z++) { d[y][z] = -1.0; } } return 0; } /// Test de la validite d'une segmentation int EstValideSeg(struct lsegment * l,float alpha) { // indice quelconque int a; // indices int i11,i12,i21,i22; // longueur cumulée int lgacc; // validité de la segmentation int ok = 1; // int which = 2; int status; double psy,sigma,S; double fisher,bound; double p = 1.0 - (double) alpha; double q = 1.0 - p; double dfn; double dfd; // calcul local de alpha double alpha_variable; // pointeurs de segments pour le parcours de la segmentation struct segment * seg1 = (struct segment *) malloc (sizeof(struct segment)); struct segment * seg2 = (struct segment *) malloc (sizeof(struct segment)); seg1 = l->premier; // vide if (ok && seg1 == NULL) { return 1; } // un segment seg2 = l->premier->suivant; if (ok && seg2 == NULL) { return 1; } // au moins deux segments if (vers == 1) { // calcul de sigma sqrt(variance) sigma = sqrt( (double)(D[k][n]) / (double)(n - k) ); // calcul de la variable de fisher dfn = k - 1; dfd = n - k; cdff(&which,&p,&q,&fisher,&dfn,&dfd,&status,&bound); } if (seuil_variable) { max_alpha[k] = 0.0; for (a=0;alg-1; i21 = lgacc+seg1->lg; i22 = lgacc+seg1->lg+seg2->lg-1; if (d[i21][i22] == -1.0) d[i21][i22] = Calculd(i21,i22); // calcul de psy (difference entre les deux moyennes) psy = (double) (moy[i11][i12] - moy[i21][i22]); if(vers == 1) { // calcul de S S = (double) sqrt( (double)(k - 1) * fisher * ( 1/((double)seg1->lg) + 1/((double)seg2->lg) ) ); } else { // calcul de sigma sqrt(variance) sigma = (double) sqrt( (d[i11][i12] + d[i21][i22]) / (seg1->lg + seg2->lg - 2) ); // calcul de la variable de fisher dfn = 1; dfd = seg1->lg + seg2->lg - 2; cdff(&which,&p,&q,&fisher,&dfn,&dfd,&status,&bound); // calcul de S S = (double) sqrt( fisher * (1/(((double)seg1->lg)) + (1/((double)seg2->lg))) ); } // test de validité de la segmentation ok = ( (psy-S*sigma > 0) || (psy+S*sigma < 0) ); if (seuil_variable) { ok = 1; alpha_variable = (double) 1.0; while (ok && alpha_variable > 0.0) { alpha_variable = alpha_variable - 0.001; p = 1.0 - (double) alpha_variable; q = 1.0 - p; if(vers == 1) { // calcul de la variable de fisher cdff(&which,&p,&q,&fisher,&dfn,&dfd,&status,&bound); // calcul de S S = sqrt( (double)(k - 1) * fisher * ( 1/((double)seg1->lg) + 1/((double)seg2->lg) ) ); } else { // calcul de sigma sqrt(variance) sigma = (double) sqrt( (d[i11][i12] + d[i21][i22]) / (seg1->lg + seg2->lg - 2) ); // calcul de la variable de fisher dfn = 1; dfd = seg1->lg + seg2->lg - 2; cdff(&which,&p,&q,&fisher,&dfn,&dfd,&status,&bound); // calcul de S S = (double) sqrt( fisher * (1/(((double)seg1->lg)) + (1/((double)seg2->lg))) ); } ok = ( (psy-S*sigma > 0) || (psy+S*sigma < 0) ); } ok = 1; talpha[k][a] = alpha_variable; a++; if (alpha_variable > max_alpha[k]) max_alpha[k] = alpha_variable; } // affichage infos if ( (!ok) && (verb) && (!seuil_variable)) { printf("\n\nSegmentation d'ordre %i rejettée par le test de Scheffe\n",k); printf("Caracteristiques : \n"); printf("\tlg seg 1 = %i\n",seg1->lg); printf("\tlg seg 2 = %i\n",seg2->lg); printf("\tmoy seg 1 = %f\n",moy[i11][i12]); printf("\tmoy seg 2 = %f\n",moy[i21][i22]); printf("\tvar seg 1 = %f\n",d[i11][i12]/seg1->lg); printf("\tvar seg 2 = %f\n",d[i21][i22]/seg2->lg); printf("\twhich = %i ; p = %f ; q (= alpha) = %f ; dfn = %f ; dfd = %f \n",which,p,q,dfn,dfd); printf("\tpsy = %f ; sigma = %f ; fisher = %f ; S = %f \n",psy,sigma,fisher,S); printf("\tpsy - S * sigma = %f ; psy + S * sigma = %f \n",psy-S*sigma,psy+S*sigma); } lgacc = lgacc + seg1->lg; } seg1 = seg2; seg2 = seg2->suivant; } return ok; } /// Lecture de la série ordonnée int Lecture(char * datafile, float s[], int * t) { // indice quelconque int i; // le flux associé aux fichiers de donnees FILE *stddata; // le flux associé au fichier de sortie FILE *standout; if ((stddata = fopen (datafile, "r")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", datafile); printf ("ERROR : Verifiez que `%s` existe et est accessible en lecture\n", datafile); return 1; } if ((standout = fopen (outputfile, "w")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", outputfile); return 1; } // Lecture du fichier et sauvegarde dans s fscanf(stddata,"%d\n",t); if ((*t) > DEFAULT) { printf("WARNING : La taille de la série est supérieure au maximum autorisé\n"); printf("WARNING : Seuls %i éléments seront pris en compte\n",DEFAULT); *t = DEFAULT; } for (i=1;i<=*t;i++) { if (!no_etiquet) fscanf(stddata,"%i",&etiqu[i]); fscanf(stddata,"%f",&s[i]); fprintf(standout,"%f\n",s[i]); } fclose (stddata); fclose (standout); return 0; } /// Ecriture des résultats int Ecriture(int k,struct lsegment * s) { // indices quelconques int y,z; // tampon de longueur de segment int lgtmp; // un pointeur de segment struct segment * segtmp = (struct segment *) malloc (sizeof(struct segment)); // tampon float tmp; // le flux tampon FILE *stdtmp; // le flux associé au fichier de sortie FILE *standout; // copie du fichier de sortie actuel if ((standout = fopen (outputfile, "r")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", outputfile); return 1; } if ((stdtmp = fopen ("outputfile.tmp", "w")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", outputfile); return 1; } rewind(standout); for (z=1;z<=n;z++) { for (y=1;y<=k;y++) { fscanf(standout,"%f",&tmp); fprintf(stdtmp,"%f ",tmp); } fprintf(stdtmp,"\n"); } fclose(standout); fclose(stdtmp); // ajout des données nouvelles if ((standout = fopen (outputfile, "w")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", outputfile); return 1; } if ((stdtmp = fopen ("outputfile.tmp", "r")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", outputfile); return 1; } rewind(stdtmp); lgtmp = 1; segtmp = s->premier; for (y=1;y<=segtmp->lg;y++) { for (z=1;z<=k;z++) { fscanf(stdtmp,"%f",&tmp); fprintf(standout,"%f\t",tmp); } fprintf(standout,"%f\n",moy[lgtmp][lgtmp-1+segtmp->lg]); } lgtmp = lgtmp+segtmp->lg; while (segtmp->suivant != NULL) { segtmp = segtmp->suivant; for (y=1;y<=segtmp->lg;y++) { for (z=1;z<=k;z++) { fscanf(stdtmp,"%f",&tmp); fprintf(standout,"%f\t",tmp); } fprintf(standout," %f\n",moy[lgtmp][lgtmp-1+segtmp->lg]); } lgtmp = lgtmp+segtmp->lg; } fclose(standout); fclose(stdtmp); return 0; } /// Ecriture des caractéristiques de l'éxecution int EcritureCarac(int k) { // indices quelconques int y,z; // tampon float tmp; // le flux tampon FILE *stdtmp; // le flux associé au fichier de sortie FILE *standout; // poitneur de liste de segment struct lsegment * lseg; // pointeur de segment struct segment * seg; // copie du fichier de sortie actuel if ((standout = fopen (outputfile, "r")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", outputfile); return 1; } if ((stdtmp = fopen ("outputfile.tmp", "w")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", outputfile); return 1; } rewind(standout); for (z=1;z<=n;z++) { for (y=1;y<=k;y++) { fscanf(standout,"%f",&tmp); fprintf(stdtmp,"%f ",tmp); } fprintf(stdtmp,"\n"); } fclose(standout); fclose(stdtmp); // ajout des données nouvelles if ((standout = fopen (outputfile, "w")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", outputfile); return 1; } if ((stdtmp = fopen ("outputfile.tmp", "r")) == NULL) { printf ("ERROR : Ouverture du fichier %s impossible\n", outputfile); return 1; } fprintf(standout,"FICHIER RESULTAT DE L'OPERATION DE SEGMENTATION\n"); fprintf(standout,"\n\nDonnées Générales\n"); // Informations globales fprintf(standout,"\nInformations globales :\n"); fprintf(standout," - nom du fichier d'entrée : %s\n",datafile); fprintf(standout," - nombre d'éléments traités : %i\n",n); fprintf(standout," - nom du fichier de sortie : %s\n",outputfile); fprintf(standout,"\nMode d'exécution :\n"); if(!verb) fprintf(standout," - mode verbeux désactivé\n"); else fprintf(standout," - mode verbeux activé\n"); if (seuil_variable) { fprintf(standout," - variation automatique de seuil activé\n - test version \"comparaison "); if (vers == 1) fprintf(standout,"globale\" (= 1"); else fprintf(standout,"locale\" (= 2"); fprintf(standout,")\n"); } else { fprintf(standout," - variation automatique de seuil desactivé\n - test "); if (!no_scheffe) { if (vers == 1) fprintf(standout,"version \"comparaison globale\" (= 1"); else fprintf(standout,"version \"comparaison locale\" (= 2"); fprintf(standout,")\n"); } else fprintf(standout,"désactivé\n"); } fprintf(standout,"\nCaractéristiques : \n"); fprintf(standout," - ordre maximal permis : %i\n",ksup); if ((!seuil_variable) && (!no_scheffe)) fprintf(standout," - seuil alpha utilisé : %.4f\n",alpha); fprintf(standout," - taille min de segment : %i\n",tmin); fprintf(standout," - ordre maximal atteint : %i\n",k-1); fprintf(standout,"\n\nCaractéristiques des Segmentations Calculées \n"); // Ajout informations associées à chaque segmentation // (indices de qualité et écart minimum obtenu) for (z=1;z1)) { if (max_alpha[z] == 1) fprintf(standout,"La segmentation ne satisfait pas le test\n"); else fprintf(standout,"La segmentation satisfait le test de Scheffe\n"); } fprintf(standout,"Ecart quadratique à la moyenne : %f\n",D[z][n]); fprintf(standout,"Longueur des segments : { "); lseg = &tlseg[z][n]; seg = lseg->premier; fprintf(standout,"%i",seg->lg); for (y=2;y<=z;y++) { seg = seg->suivant; fprintf(standout," , %i",seg->lg); } fprintf(standout," }\n"); if (seuil_variable && (z>1)) { tmp = 0.0; fprintf(standout,"Indices de confiance : { "); tmp = tmp + ((1.0 - talpha[z][1]) * 100); fprintf(standout,"%.3f",(1.0 - talpha[z][1]) * 100); for (y=2;y= 2) // Utilisation par ligne de commande { if (argc == 2) { option = argv[1]; if (strcmp(option,print_h) == 0 || strcmp(option,print_help) == 0 || strcmp(option,print_helpp) == 0) { printf(usage); exit(-1); } } // Découpage des options for(i=1;i= 1)) { printf("ERROR : La valeur specifiee '%f' ne correspond pas a une \n", alpha); printf("ERROR : valeur attendue pour le seuil alpha du test de Scheffe\n"); printf("ERROR : Veuillez utiliser une valeur reelle dans l'intervalle ]0;1[\n"); printf("ERROR : Des valeurs classiques sont, par exemple, '.01' ou '.002'\n"); exit(-1); } } else if (entier == 2) // test de scheffe variable { fscanf(standin,"%i\n",&vers); if ((vers != 1) && (vers != 2)) { printf("ERROR : La valeur specifiee '%i' ne correspond pas a une valeur\n", vers); printf("ERROR : attendue pour designer une version du test de Scheffe\n"); printf("ERROR : Veuillez utiliser la valeur '1' ou '2' designant\n"); printf("ERROR : respectivement la version globale ou locale du test\n"); exit(-1); } } else // mauvaise version { printf("ERROR : La valeur specifiee '%i' ne correspond pas a une valeur\n", entier); printf("ERROR : attendue pour designer le mode d'execution du programme\n"); printf("ERROR : Veuillez utiliser la valeur '0', '1' ou '2' designant\n"); printf("ERROR : respectivement la desactivation du test du Scheffe,\n"); printf("ERROR : le test a seuil statique ou a seuil dynamique\n"); exit(-1); } // nombre de segmentation maximal fscanf(standin,"%i\n",&segmax); if (segmax <= 0) { printf("ERROR : L'ordre de segmentation maximal specifie '%i' est incorrect\n", segmax); printf("ERROR : On attend une valeur entiere strictement positive\n"); exit(-1); } fscanf(standin,"%s",tampon); if (strcmp(tampon,"t") == 0) // taille min absolue { fscanf(standin,"%i\n",&tmin); if (tmin <= 0) { printf("ERROR : La taille minimale de segment specifiee '%i' est incorrecte\n", tmin); printf("ERROR : On attend une valeur entiere strictement positive \n"); printf("ERROR : Une valeur interessante sera plusieurs fois inferieure"); printf("ERROR : a la taille de la serie complete (au moins 2 a 3 fois)\n"); exit(-1); } } else if (strcmp(tampon,"p") == 0) // taille min % { fscanf(standin,"%f\n",&pmin); if ((pmin <= 0.0) || (pmin >=100.0)) { printf("ERROR : Le pourcentage minimal specifie '%.2f%%' est incorrect\n", pmin); printf("ERROR : On attend un reel compris dans l'intervalle ]0;100[ \n"); exit(-1); } } else { printf("ERROR : La valeur '%s' est incorrecte\n", tampon); printf("ERROR : Veuillez utiliser 't' ou 'p' suivi respectivement de la\n"); printf("ERROR : taille minimale de segment ou du pourcentage minimal\n"); printf("ERROR : qu'elle doit representer par rapport a la serie complete\n"); exit(-1); } // etiquettes fscanf(standin,"%i\n",&no_etiquet); if ((no_etiquet != 0) && (no_etiquet != 1)) { printf("ERROR : La valeur specifiee '%i' relativement aux etiquettes est incorrecte\n", no_etiquet); printf("ERROR : Veuillez utiliser la valeur '0' ou '1' signifiant respectivement\n"); printf("ERROR : l'absence ou la presence d'etiquettes associees aux elements de\n"); printf("ERROR : la serie dans le fichier '%s'\n",datafile); exit(-1); } no_etiquet = 1 - no_etiquet; // mode verbeux fscanf(standin,"%i\n",&verb); if ((verb != 0) && (verb != 1)) { printf("ERROR : La valeur specifiee '%i' relativement mode verbeux est incorrecte\n", verb); printf("ERROR : Veuillez utiliser la valeur '0' ou '1' designant respectivement\n"); printf("ERROR : la desactivation ou l'activation du mode verbeux\n"); exit(-1); } // nom du fichier de sortie outputfile = (char *) malloc(26 * sizeof(char)); fscanf(standin,"%s\n",outputfile); } // Lecture de la série if (Lecture(datafile,serie,&n) != 0) { printf("ERROR : La lecture de la série a échoué\n"); return 1; } // conversion de la taille minimale de segment // en terme de nombre d'element plutot qu'en // pourcentage si besoin est if (pmin != 0) if ( tmin < (int)floor(((float)n) * pmin / 100.0) ) // arrondi à la valeur entière la plus proche tmin = (int)floor(((float)n) * pmin / 100.0); // gestion de la taille minimale des segments // dans le cas du test de Scheffe local if ((entier == 1) || (entier == 2)) // test de Scheffe active if (vers == 2) // version locale du test de Scheffe if ((((float)tmin) * 100.0 / ((float)n)) < 2.0) // la taille minimale des segments est trop petite { printf("WARNING : Vous avez choisi d'utiliser le test de Scheffe dit 'local'\n"); printf("WARNING : Pour que ce test soit significatif, une taille minimale\n"); printf("WARNING : de segment de 2%% de la taille de la serie globale est requise\n"); printf("WARNING : La taille de segment specifiee (%i elements) est donc trop faible\n",tmin); printf("WARNING : L'execution va se poursuivre en utilisant la valeur minimale\n"); tmin = (int)floor(((float)n) * 2.0 / 100.0); printf("WARNING : possible (ce qui represente %i elements dans le cas present)\n",tmin); } // gestion de la borne superieure de variation // de l'ordre de segmentation ksup = n; if (ksup > segmax) ksup = segmax; if (ksup > n/tmin) { ksup = n/tmin; printf("WARNING : Compte tenu de la taille de la serie et de la taille'\n"); printf("WARNING : minimale de segment autorisee, l'ordre maximal de'\n"); printf("WARNING : segmentation possible sera de '%i' (au lieu de celui'\n",ksup); printf("WARNING : specifie dans la requete, a savoir '%i')'\n", segmax); } return 0; } /// Affichage de la liste de segments void Afficherlseg(struct lsegment * s) { int lgtmp; struct segment * segtmp = (struct segment *) malloc (sizeof(struct segment)); segtmp = s->premier; if (segtmp == NULL) printf("La liste est vide\n"); else { lgtmp = 1; printf("\n{\n lg = %i , moy[%i][%i] = %.2f ",segtmp->lg,lgtmp,lgtmp-1+segtmp->lg,moy[lgtmp][lgtmp-1+segtmp->lg]); lgtmp = lgtmp+segtmp->lg; while (segtmp->suivant != NULL) { segtmp = segtmp->suivant; printf(";\n lg = %i , moy[%i][%i] = %.2f ",segtmp->lg,lgtmp,lgtmp-1+segtmp->lg,moy[lgtmp][lgtmp-1+segtmp->lg]); lgtmp = lgtmp+segtmp->lg; } printf("\n} ; "); } }