PERL

Résolu
Bonjour,

J'ai besoin de vos lumières ...
J'ai un fichier que je place dans un tableau avec plusieurs colonnes. L'une des colonnes contient des valeurs différentes. Par exemple :

145
175
652
854
956852
452
615
245

Mon problème c'est la valeur immense, ici 956852. Je voudrais qu'en cas de valeurs énorme, elle soit remplacer par 0 ou la valeur précédente (ici 854).

Merci d'avance pour votre aide
Configuration: Windows Vista / Firefox 3.5.8

18 réponses

Résumé de la discussion

Le problème porte sur le remplacement d'une valeur extrêmement grande dans une colonne d'un fichier, afin que, en cas de valeur énorme, elle soit remplacée par 0 ou par la valeur précédente. Des propositions en Perl et d'autres approches proposent d'identifier les valeurs atypiques par la longueur du champ et de remplacer les hors-norme par 0 ou par la valeur précédente. Certaines solutions utilisent un comptage des longueurs pour déterminer un seuil, puis remplacent les valeurs hors seuil par 0, ou affichent les valeurs précédentes selon le contexte. Des échanges ont aussi abordé les cas limites et les tests, avec des scripts Perl et des discussions sur la gestion des doublons de longueur et des premiers éléments sans valeur précédente.

Bobot (l’IA à votre service)
  1. Modérateur
    Re,

    Ok, c'est bien.
    En fait je parcours une fois le fichier et je stocke dans un hash
    la cle = la longueur de champ
    la valeur = un compteur qui donnera le NLignes

    Maintenant on a ce qu'il nous faut (NChiffres et NLignes) pour pouvoir mettre la condition.

    Ensuite je garde la valeur la plus présente (dans ce cas le 2 est 3 fois)
    Et puis c'est simple
    Toutes les champs plus petits (on teste la longueur, c'est suffisant) que 4 puisqu'on accepte 2-1 et 2+1
    Il fallait que je fasse directement $max+2 au lieu d'ajouter deux fois 1
    lami20j@debian-acer:~/perl$ cat colonne6.pl 
    #!/usr/bin/perl
    use strict;use warnings;
    my (@col,%h);
    my $f="col5.txt";
    open F,"$f" or die "E/S : $!\n";
    
    while(<F>){
      chomp;
      my @l=split;
      $h{length($l[2])}++;
    }
    my @tri =sort {$h{$b} <=> $h{$a}} keys %h;
    my $max = shift @tri;
    open F,"$f" or die "E/S : $!\n";
    while(<F>){
      chomp;
      my @l=split;
      (length($l[2])<$max+2)?print "$_\n":print "@l[0..1] 0\n";
    }
    


    Mais je viens de penser à un cas qu'on a oublié. Si jamais il y a deux champs avec NLignes égal ou plusieurs, on choisi quoi?
    Puisque sinon le plus petit est pris en compte.
    lami20j@debian-acer:~/perl$ cat col5.txt 
    100108 17:00 64
    100108 18:00 64
    100108 18:00 28
    100104 01:00 333
    100108 18:00 3334
    100108 18:00 5
    100108 18:00 7
    100108 18:00 55555
    100108 18:00 55555
    100108 18:00 55555
    lami20j@debian-acer:~/perl$ perl colonne6.pl
    100108 17:00 64
    100108 18:00 64
    100108 18:00 28
    100104 01:00 333
    100108 18:00 0
    100108 18:00 5
    100108 18:00 7
    100108 18:00 0
    100108 18:00 0
    100108 18:00 0
    

    1. Merci pour les explications, shift sert à quoi ?

      Pour le cas que l'on a oublié, je ne pense pas qu'il se produira ou devrais je dire je suis certaines qu'il ne se produira pas.
    2. Modérateur
      @wafa_oRe,

      Le tableau @tri contient la fréquence d'apparition
      2->3 fois
      etc.
      $max = 2 le 1er élément de tableau qu'on obtiens avec shift, j'aurai pu utiliser directement $tri[0].

      Voir la fonction shift

      Tu peux utiliser des print dans le script pour voir ce qui se passe.
      Pour mieux comprendre ce que fait le script voilà
      lami20j@debian-acer:~/perl$ cat colonne6.pl 
      #!/usr/bin/perl
      use strict;use warnings;
      my (@col,%h);
      my $f="col5.txt";
      open F,"$f" or die "E/S : $!\n";
      
      while(<F>){
        chomp;
        my @l=split;
        $h{length($l[2])}++;
      }
      print "NChiffre: $_ => NLignes: $h{$_}\n" for keys %h;
      my @tri =sort {$h{$b} <=> $h{$a}} keys %h;
      print "\@tri: @tri\n";
      my $max = shift @tri;
      print "On choisi la valeur \$max = $max qui est sur $h{$max} NLignes\n";
      print "Ce qui reste dans le tableau après shift \@tri: @tri\n";
      #open F,"$f" or die "E/S : $!\n";
      #while(<F>){
      #  chomp;
      #  my @l=split;
      #  (length($l[2])<$max+2)?print "$_\n":print "@l[0..1] 0\n";
      #}
      lami20j@debian-acer:~/perl$ perl colonne6.pl
      NChiffre: 1 => NLignes: 2
      NChiffre: 4 => NLignes: 1
      NChiffre: 3 => NLignes: 1
      NChiffre: 2 => NLignes: 3
      @tri: 2 1 4 3
      On choisi la valeur $max = 2 qui est sur 3 NLignes
      Ce qui reste dans le tableau après shift @tri: 1 4 3
      

      Et je pense que tu peux mettre en résolu.

  2. Modérateur
    Re,

    Ok.
    Ca te va?
    lami20j@debian-acer:~/perl$ cat col5.txt 
    100108 17:00 64
    100108 18:00 64
    100108 18:00 28
    100104 01:00 333
    100108 18:00 3334
    100108 18:00 5
    100108 18:00 7
    lami20j@debian-acer:~/perl$ cat colonne6.pl 
    #!/usr/bin/perl
    use strict;use warnings;
    my (@col,%h);
    my $f="col5.txt";
    open F,"$f" or die "E/S : $!\n";
    
    while(<F>){
      chomp;
      my @l=split;
      $h{length($l[2])}++;
    }
    
    my @tri =sort {$h{$b} <=> $h{$a}} keys %h;
    my $max = (shift @tri) + 1;
    
    open F,"$f" or die "E/S : $!\n";
    while(<F>){
      chomp;
      my @l=split;
      (length($l[2])<$max+1)?print "$_\n":print "@l[0..1] 0\n";
    }
    lami20j@debian-acer:~/perl$ perl colonne6.pl
    100108 17:00 64
    100108 18:00 64
    100108 18:00 28
    100104 01:00 333
    100108 18:00 0
    100108 18:00 5
    100108 18:00 7
    
    1. :D Ca fonctionne !!!!!!!!!!!!!
      Merci. Il ne me reste plus qu'à comprendre ton programme ...
  3. Modérateur
    Re,

    Alors il faut parcourir une fois le fichier pour trouver le NChiffres, puisqu'on ne pas savoir d'avance le min, et ensuite c'est facile.
    Tes fichiers peuvent contenir jusqu'à quel nombre de lignes?
    1. Modérateur
      Re,

      Je pense toujours que la multiplication n'est pas obligatoire.

      Dans ce cas, les valeurs considérées comme "normale" sont les nombres à 4 chiffres car elles sont plus nombreuses. Toutefois, on peut accepter les nombres de 4-1 chiffres et 4+1 chiffres

      Donc la règle est alors
      - le nombre de chiffre en fonction du nombre le plus grande de ligne qui contient ce numéro, Nchiffres sur NLignes
      - on accepte le NChiffres-1 et +1

      C'est ça la règle?

      Alors en ce cas

      64
      54
      28
      333
      3344
      5
      7

      NChiffres = 2 puisque NLignes=3
      et on accepte NChiffres=1 et NChiffres=3

      On doit obtenir

      64
      54
      28
      333
      0
      5
      7

      C'est ça?
      1. Modérateur
        Re,

        Quand la 1ère ligne ne rempli pas la condition, il n'y a pas de valeur précédente : Ca m'embete un peu. J'ai eu le même problème dans le programme que j'ai posté. Une valeur n'est pas testée ...

        Oui, ça t'embête, je suis d'accord, mais tu veux mettre quoi à la place.
        En revanche avec 0 à la place ça ne pose pas de problèmes, puisque pas besoin de valeur précédente
        lami20j@debian-acer:~/perl$ cat col4.txt 
        100104 01:00 106909268
        100108 17:00 19758
        100108 18:00 25773
        100108 18:00 85174
        100104 01:00 106909268
        100108 18:00 11650
        100108 18:00 19597
        100108 18:00 23299
        lami20j@debian-acer:~/perl$ perl colonne5.pl col4.txt
        Max accepté : 6
        100104 01:00 0
        100108 17:00 19758
        100108 18:00 25773
        100108 18:00 85174
        100104 01:00 0
        100108 18:00 11650
        100108 18:00 19597
        100108 18:00 23299
        

        1. Modérateur
          Re,

          lami20j@debian-acer:~/perl$ cat colonne5.pl 
          #!/usr/bin/perl
          use strict;use warnings;
          my @col;
          print "Max accepté : ";
          chomp(my $n=<STDIN>);
          while(<>){
            chomp;
            my @l=split;
            (length($l[2])<$n)?print "$_\n":print "@l[0..1] 0\n";
          }
          lami20j@debian-acer:~/perl$ perl colonne5.pl col1.txt
          Max accepté : 4
          100108 19:00 519
          100108 19:00 625
          100105 19:00 399
          100105 17:00 398
          100105 19:00 433
          100108 19:00 352
          100107 18:00 430
          lami20j@debian-acer:~/perl$ perl colonne5.pl col2.txt
          Max accepté : 6
          100108 17:00 19758
          100108 18:00 25773
          100108 18:00 85174
          100104 01:00 0
          100108 18:00 11650
          100108 18:00 19597
          100108 18:00 23299
          lami20j@debian-acer:~/perl$ perl colonne5.pl col3.txt
          Max accepté : 7
          100104 22:00 833308
          100107 19:00 237479
          100107 19:00 0
          100110 23:00 625874
          100110 23:00 740779
          100106 22:00 0
          
          1. Merci lami20j, j'avais pas vu tes messages. Je suis partie sur la piste de xav3601 mais toujours avec des difficultés ....
            1. Modérateur
              Re,

              Tu ne dis en revanche si ça te conviens ce que je t'ai donné.
          2. Modérateur
            Re,

            Le cas 2 amélioré (je garde dans le tableau que le 1er élément qui sera la valeur affichée).
            Le script précédent augmenterait le tableau d'un pour chaque élément de longueur plus petit que 4.
            Dans cette version le tableau contient un seul élément
            lami20j@debian-acer:~$ cat perl/colonne2.pl
            #!/usr/bin/perl
            use strict;use warnings;
            my @col;
            while(<DATA>){
              chomp;
              unshift @col,$_ if length($_)<4; 
              splice(@col,1);
              print @col+0,": @col\n";
            }
            __END__
            145
            175
            956852
            652
            854
            956852
            956852
            452
            615
            956852
            956852
            956852
            956852
            956852
            245
            956852
            567
            lami20j@debian-acer:~$ perl perl/colonne2.pl
            1: 145
            1: 175
            1: 175
            1: 652
            1: 854
            1: 854
            1: 854
            1: 452
            1: 615
            1: 615
            1: 615
            1: 615
            1: 615
            1: 615
            1: 245
            1: 245
            1: 567
            
            1. En fait, je ne comprends pas ton programme.
              J'ai essayé ca :

              #!/usr/bin/perl
              use strict;use warnings;
              my @col;
              while(<DATA>){
              chomp;
              unshift @col,$_ if length($_)<7;
              splice(@col,1);
              print @col+0,": @col\n";
              }

              __END__
              109190
              188058
              178240
              102657
              86015
              197589
              106909268
              116507
              195971
              232993
              51982476
              18927525

              Et voilà le résultat :
              1: 109190
              1: 109190
              1: 109190
              1: 109190
              1: 86015
              1: 86015
              1: 86015
              1: 86015
              1: 86015
              1: 86015
              1: 86015
              1: 86015
            2. Modérateur
              @wafa_oJe te l'ai dit que je ne comprends pas ce que tu veux.
              Pourquoi 7 et pas 6?
              Comment tu choisi le minimum?
              Et puis comme toujours on ne travaille pas sur le même fichier.

              Voici ce que j'ai avec ton exemple, ça semble bon chez moi, non?
              Je vais t'expliquer l'algo de programme, mais d'abord il faut qu'on fait les tests sur le même fichier.
              lami20j@debian-acer:~$ cat perl/colonne2.pl 
              #!/usr/bin/perl
              use strict;use warnings;
              my @col;
              while(<DATA>){
                chomp;
                unshift @col,$_ if length($_)<7; 
                splice(@col,1);
                print @col+0,": @col\n";
              }
              __END__
              109190
              188058
              178240
              102657
              86015
              197589
              106909268
              116507
              195971
              232993
              51982476
              18927525
              lami20j@debian-acer:~$ perl perl/colonne2.pl 
              1: 109190
              1: 188058
              1: 178240
              1: 102657
              1: 86015
              1: 197589
              1: 197589
              1: 116507
              1: 195971
              1: 232993
              1: 232993
              1: 232993
              
            3. Modérateur
              @wafa_oRe,

              Pour comprendre ce que le programme fait regarde ça
              T'as bien compris que dans le tableau je mets tout sauf le immense, ce qui permettra de garder une trace sur le précedent
              splice permet tout simplement de garder dans le tableau que le 1 élément, c'est ce qui nous intéresse (regarde en gras)

              lami20j@debian-acer:~/perl$ cat colonne3.pl 
              #!/usr/bin/perl
              use strict;use warnings;
              my @col;
              while(<DATA>){
                chomp;
                unshift @col,$_ if length($_)<7; 
                print "\@col : @col\n";
              }
              __END__
              109190
              188058
              178240
              102657
              86015
              197589
              106909268
              116507
              195971
              232993
              51982476
              18927525
              lami20j@debian-acer:~/perl$ perl colonne3.pl
              @col : 109190
              @col : 188058 109190
              @col : 178240 188058 109190
              @col : 102657 178240 188058 109190
              @col : 86015 102657 178240 188058 109190
              @col : 197589 86015 102657 178240 188058 109190
              @col : 197589 86015 102657 178240 188058 109190
              @col : 116507 197589 86015 102657 178240 188058 109190
              @col : 195971 116507 197589 86015 102657 178240 188058 109190
              @col : 232993 195971 116507 197589 86015 102657 178240 188058 109190
              @col : 232993 195971 116507 197589 86015 102657 178240 188058 109190
              @col : 232993 195971 116507 197589 86015 102657 178240 188058 109190
              
            4. Modérateur
              @lami20jTu as aussi remarqué que l'élément est rajouté au début ce qui veut dire que
              1ère ligne :                     1er élément
              2ème ligne : si condition alors  2eme élément 1 élément
                           sinon               1er élément - donc je garde le précedent
              3ème ligne : si condition alors  3ème élément 2eme élément 1 élément
                           sinon alors         le contenu de tableau ne change pas
              
              etc.
              

              Toutefois il y a un cas qui ne sera rempli, quand la 1ère ligne ne rempli pas la condition, il n'y a pas de valeur précédente.
            5. @lami20jJe comprends mieux merci.

              Pourquoi 6 ou 7 et pas 3 ? je t'explique. En fait, j'ai plusieurs fichiers. Pour certains, c'est 3, d'autres 2, d'autres 8, d'autres 7. Je ne veux pas fixer la valeur, enfin je devrais dire que je ne peux pas. Ces fichiers changent tout le temps. Une fois j'ai des nombres composés de 3 chiffres, une autre fois les nombres sont composés de 8 chiffres. C'est indépendant de ma volonté, je reçois les fichiers comme ca.
              C'est pour celà que je suis partie sur le script que j'ai posté.

              Quand la 1ère ligne ne rempli pas la condition, il n'y a pas de valeur précédente : Ca m'embete un peu. J'ai eu le même problème dans le programme que j'ai posté. Une valeur n'est pas testée ...
          3. Modérateur
            Salut,

            Comme tu ne veux pas donner la structure de ton fichier pour savoir sur quelle colonne on doit réagir, alors je te laisse adapter.

            J'ai ajouter plusieurs lignes et aussi des lignes consécutives avec des valeurs plus grandes que 999 pour tester

            Voici les deux cas
            Cas 1: remplacer avec zéro si la longueur est plus grande que 3
            lami20j@debian-acer:~$ cat colonne0.pl 
            #!/usr/bin/perl
            use strict;use warnings;
            while(<DATA>){
              /\d{4,}/?print "0\n":print;
            }
            __END__
            145
            175
            652
            854
            956852
            452
            615
            956852
            245
            956852
            956852
            956852
            lami20j@debian-acer:~$ perl colonne0.pl
            145
            175
            652
            854
            0
            452
            615
            0
            245
            0
            0
            0
            


            Cas 2: remplacer avec la valeur précédente si la longueur est plus grande que 3

            lami20j@debian-acer:~$ cat colonne1.pl 
            #!/usr/bin/perl
            use strict;use warnings;
            my @col;
            while(<DATA>){
              chomp;
              unshift @col,$_ if length($_)<4; 
              print "$col[0]\n";
            }
            __END__
            145
            175
            956852
            652
            854
            956852
            956852
            452
            615
            956852
            956852
            956852
            956852
            956852
            245
            956852
            567
            lami20j@debian-acer:~$ perl colonne1.pl
            145
            175
            175
            652
            854
            854
            854
            452
            615
            615
            615
            615
            615
            615
            245
            245
            567
            
            1. Les deux scripts pourraient fonctionner, seulement je me pose une question et j'aurais du y réfléchir avant. Ca fonctionne pour un de mes fichiers mais pas pour les autres car les valeurs sont supérieurs à trois chiffres. Ici le script fonctionnerait si j'avais indiqué 7 au lieu de 4.
              Pour ces données ca ne fonctionne pas,
              109190
              188058
              178240
              102657
              86015
              197589
              106909268
              116507
              195971
              232993
              51982476
              18927525

              En résumé, ton script réponds parfaitement à mon problème. C'est mon problème qui est mal expliqué :(
          4. Et ben avant d'insérer une valeur tu multiplie la valeur précédente par 10 et tu fais un test!
            si la valeur est supérieur au résultat tu mets la valeur précédentes sinon tu met ta valeur courante ^^

            Attention, a ne pas faire sur la premiere valeur ^^
            1. C'est ce que j'ai fait. Je récupère la valeur précédente, je la multiplie par 100. Mon problème c'est la première valeur du test. Ici je recupère 189275. Comment faire quand cette valeur n'est pas bonne.

              Ici ca fonctionne :

              #!/usr/bin/perl
              my @tab;

              foreach(<DATA>){
              chomp($_);
              my @v=split(/ /,$_);
              push (@valeur,$v[0]);
              }
              close(DATA);

              for (my $j=0; $j<@valeur; $j++)
              {
              $j--;
              my $essai= 100*$valeur[$j];
              $j++;

              if($valeur[$j]>$essai) {
              print "0\n";
              }
              else {print "$valeur[$j]\n";}
              }

              __END__
              109190
              188058
              178240
              102657
              86015
              197589
              106909268
              116507
              195971
              232993
              51982476
              189275

              Résultat :
              109190
              188058
              178240
              102657
              86015
              197589
              0
              116507
              195971
              232993
              0
              189275

              ==> tout est OK

              Ici ca ne fonctionne pas :
              #!/usr/bin/perl
              my @tab;

              foreach(<DATA>){
              chomp($_);
              my @v=split(/ /,$_);
              push (@valeur,$v[0]);
              }
              close(DATA);

              for (my $j=0; $j<@valeur; $j++)
              {
              $j--;
              my $essai= 100*$valeur[$j];
              $j++;

              if($valeur[$j]>$essai) {
              print "0\n";
              }
              else {print "$valeur[$j]\n";}
              }

              __END__
              109190
              188058
              178240
              102657
              86015
              197589
              106909268
              116507
              195971
              232993
              51982476
              18927525

              Résultat :
              109190
              188058
              178240
              102657
              86015
              197589
              0
              116507
              195971
              232993
              0
              18927525

              ==> PROBLEME, je veux avoir :
              Résultat :
              109190
              188058
              178240
              102657
              86015
              197589
              0
              116507
              195971
              232993
              0
              0
          5. Je ne suis pas douée en explication :((( . Je vais essayer de reprendre...

            Voilà les valeurs dans ma table :
            145
            175
            652
            854
            956852
            452
            615
            245

            On est d'accord que 956852 est la valeur immense.
            Dans la colonne on trouve des valeurs de l'ordre des centaines, soit un nombre à trois chiffres. Lorsque le nombre est composé de plus de 3 chiffres, il y a une erreur.
            452 est composé de 3 chiffres. On le garde.
            854 est composé de 3 chiffres. On le garde.
            956852 est composé de 6 chiffres. On NE le garde PAS car il y a plus de 3 chiffres.

            :s :s J'espère avoir été plus claire
            1. Modérateur
              Re,

              Par exemple si la valeur est 10 fois supérieur aux autres valeurs,
              Je ne comprends pas ;-(
              1. Immense c'est à dire 10 fois les valeurs que l'on trouve dans la colonne.
                Par exemple si la valeur est 10 fois supérieur aux autres valeurs, elle est considérée comme "immense".
                Il y a 5 colonnes séparées par des espaces. Enfin l'histoire des colonnes, c'est à titre indicatif. Même si tu prends l'exemple d'une colonne, je pourrais toujours l'adapter ensuite.

                PS : C'était pour eveiller la curiosité mais j'ai compris, ne plus recommencer ;-)
                1. Modérateur
                  Salut,

                  Qu'appelles tu immense? Il y a un minimum accepté?
                  Et le fait de dire que tu as une colonne ça n'aide pas trop?
                  Quelle colonne? Il y a quoi comme séparateur entre les champs?

                  P.S. Pour ne pas dire que le titre de message ne dit rien ou il dit trop ;-)