IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

Agrégation, Regroupement de lignes identiques dans un même fichier


Sujet :

Langage Perl

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 32
    Points : 30
    Points
    30
    Par défaut Agrégation, Regroupement de lignes identiques dans un même fichier
    Bonjour,

    Je souhaite agréger (regourper) les lignes ayant des valeurs identiques
    - Sur les champs 6, 7, 8
    - Puis réaliser la Somme sur le champ 24
    - Sur une volumétrie de lignes > 1 Millions

    J'ai essayé quelque chose, mais ça ne fonctionne pas. Le split semble bien fonctionner, mais aucun regroupement n'est fait

    Le fichier d'entrée (fichier TEXT de longueur fixe)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    57  55  3 4290281  508  101  209  202  0  10001  7  629 102 0 24   25 3455    0   0  0 1782   0  0     0   1200    AUG 31 2012 11:41AM
    57  55  3 4261131  508  101  209  202  0  10000  7  629 102 0 24   25 2574    0   0  0 7777   0  50679 0   4934    AUG 31 2012 11:41AM
    57  55  3 4642611  508  101  209  201  0  10000  7  629 102 0 24   25 3455    0   0  0 8924   0  50677 0   400     AUG 31 2012 11:41AM
    57  55  3 4991920  508  100  209  202  0  10001  7  629 102 0 24   25 5811    0   0  0 2896   0  0     0   10266   AUG 31 2012 11:41AM
    Mon Code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    #!/usr/bin/perl
    use strict;
    use warnings;
    use Tie::File;
     
    my (@file, @result);
    open(IN, "test.txt") || die "E/S : $!";
    while(<IN>){
            my $tmp = substr($_, 3);
            push(@file, $tmp);
    }
    while(@file){
            my $line = $file[0];
            my @line= split (/\s+/,$line);
            my $somme_montant = $line[24];
            shift(@file);
            my $i = 0;
            foreach (@file){
                    if($_ =~ /$line[5] $line[6] $line[7](\d+)/){
                            $somme_montant = $somme_montant + $1;
                            splice(@file, $i, 1);
                    }
            $i++
            }
            my $newLine = "$line[5] $line[6] $line[7] $somme_montant $i";
            push(@result, $newLine);
    }
     
    close(IN);
     
    open(OUT, ">out") || die "E/S : $!";
    foreach (@result){
                    print OUT $_."\n";
    }
    close(OUT);
    Le résultat que ça me donne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    101 209 202 1200 3 
    101 209 202 4934 2 
    101 209 201 400 1  
    100 209 202 10266 0
    En Perl v5.10.1, sur SunOS
    A l'aide

  2. #2
    Membre éprouvé Avatar de Gardyen
    Homme Profil pro
    Bio informaticien
    Inscrit en
    Août 2005
    Messages
    637
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bio informaticien

    Informations forums :
    Inscription : Août 2005
    Messages : 637
    Points : 1 050
    Points
    1 050
    Par défaut
    est-ce que ton fichier est ordonné (= les lignes avec les mêmes champ 5 6 7 sont toutes les unes à la suite des autres) ? ou bien alors ces lignes peuvent être trouvées n'importe où dans le fichier ?
    Nous les geeks, c'est pas qu'on a une case en moins, c'est juste qu'on compte à partir de zéro.
    Plus les choses changent, plus elles restent les mêmes

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 32
    Points : 30
    Points
    30
    Par défaut
    Non, le fichier n'est pas ordonné sur les champs 5 6 7.
    On peut les trouver n'importe où dans le fichier

  4. #4
    Membre éprouvé Avatar de Gardyen
    Homme Profil pro
    Bio informaticien
    Inscrit en
    Août 2005
    Messages
    637
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bio informaticien

    Informations forums :
    Inscription : Août 2005
    Messages : 637
    Points : 1 050
    Points
    1 050
    Par défaut
    tiens voilà un premier script pas trop optimisé (ni très beau ), dis-moi ce que tu en penses
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    open(IN, "data.txt");
    my %all;
    while (<IN>) {
    	my @line = split /\s+/;
    	$all{$line[5]}{$line[6]}{$line[7]} += $line[24];
    }
    close(IN);
     
    foreach my $a (keys %all) {
     
    	foreach my $b (keys %{$all{$a}}) {
     
    		foreach my $c (keys %{$all{$a}{$b}}) {
    			my $montant = $all{$a}{$b}{$c};
    			print "$a $b $c $montant\n";
    		}
    	}
    }
    ce qui me donne sur ton fichier d'exemple:
    101 209 201 400
    101 209 202 6134
    100 209 202 10266
    juste une question, que représente ton $i ?
    Nous les geeks, c'est pas qu'on a une case en moins, c'est juste qu'on compte à partir de zéro.
    Plus les choses changent, plus elles restent les mêmes

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 32
    Points : 30
    Points
    30
    Par défaut
    Merci pour ton post Gardyen

    Je viens d'essayer ton pl

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
    #!/usr/bin/perl
     
    use strict;
    use warnings;
    use Tie::File;
    use utf8;
    open(IN, "data.txt");
    my %all;
    while (<IN>) {
    	my @line = split /\s+/;
    	$all{$line[5]}{$line[6]}{$line[7]} += $line[24];
    }
    close(IN);
     
    foreach my $a (keys %all) {
     
    	foreach my $b (keys %{$all{$a}}) {
     
    		foreach my $c (keys %{$all{$a}{$b}}) {
    			my $montant = $all{$a}{$b}{$c};
    			print "$a $b $c $montant\n";
    		}
    	}
    }
    Mais il sort en KO avec le message suivant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Unrecognized character \xC2; marked by <-- HERE after <-- HERE near column 1 at agregation.pl line 14.
    Je vais essayer de regarder pourquoi, mais ça semble un problème de jeu de caractères. j'ai essayé de forcer l'utf8, mais pareil
    Si tu as une idée je suis preneur

  6. #6
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 32
    Points : 30
    Points
    30
    Par défaut
    J'allais oublier, pour répondre à ta question "$i", c'était pour remonter le nombre de lignes identiques

  7. #7
    Membre éprouvé Avatar de Gardyen
    Homme Profil pro
    Bio informaticien
    Inscrit en
    Août 2005
    Messages
    637
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bio informaticien

    Informations forums :
    Inscription : Août 2005
    Messages : 637
    Points : 1 050
    Points
    1 050
    Par défaut
    hum c'est un problème d'encodage qui apparait en cas de copier/coller entre différents OS... enfin il me semble

    plutôt que de le copier essaie de le taper à la main, heureusement il est pas trop long

    si tu veux récupérer le nombre de lignes, une petite modification suffit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #!/usr/bin/perl
     
    use strict;
    use warnings;
    use Tie::File;
    use utf8;
    open(IN, "data.txt");
    my %all;
    while (<IN>) {
    	my @line = split /\s+/;
    	$all{$line[5]}{$line[6]}{$line[7]}{'montant'} += $line[24];
    	$all{$line[5]}{$line[6]}{$line[7]}{'cpt'}++;
    }
    close(IN);
     
    foreach my $a (keys %all) {
     
    	foreach my $b (keys %{$all{$a}}) {
     
    		foreach my $c (keys %{$all{$a}{$b}}) {
    			my $montant = $all{$a}{$b}{$c}{'montant'};
    			my $cpt = $all{$a}{$b}{$c}{'cpt'};
    			print "$a $b $c $montant $cpt\n";
    		}
    	}
    }
    Nous les geeks, c'est pas qu'on a une case en moins, c'est juste qu'on compte à partir de zéro.
    Plus les choses changent, plus elles restent les mêmes

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 32
    Points : 30
    Points
    30
    Par défaut
    Impec Gardyen
    C'est bien mon "copier/coller" dans vi qui posait problème

    Je viens de faire le test sur un fichier de 800.000 lignes = 2 minutes de traitement, ~ 60% d(utilisation sur 1 CPU & 132 Mo utilisé

    Si je comprend bien ta solution, tu parcours autant de fois le fichier que tu as de clés (dans mon cas 3).

    Si je trie mon fichier, sur les 3 champs avant, un seul passage serait nécessaire ou l'utilisation d'une autre méthodo.
    J'essaie de trouver le meilleur algo qui me permettrait de gagner du temps

  9. #9
    Membre éprouvé Avatar de Gardyen
    Homme Profil pro
    Bio informaticien
    Inscrit en
    Août 2005
    Messages
    637
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bio informaticien

    Informations forums :
    Inscription : Août 2005
    Messages : 637
    Points : 1 050
    Points
    1 050
    Par défaut
    je ne parcours qu'une seule fois ton fichier, dans la boucle while(<IN>)

    je stocke le montant (et le nombre de lignes rencontrées pour le 2e exemple) dans un hash en me servant des 3 valeurs comme clés imbriquées (un arbre en quelque sorte)

    ensuite j'ai besoin des 3 foreach imbriqués pour afficher les résultats, cette partie-là est largement améliorable, je suis allé au plus simple :p

    si le fichier est trié sur ces 3 clés, alors plus besoin de stocker les valeurs dans un hash, il suffit de garder les valeurs précédentes en mémoire et de les afficher quand on change de valeurs => moins de mémoire utilisée

    je peux te faire un script rapide avec l'hypothèse du fichier trié, le principal problème sera de trier ce fameux fichier...
    Nous les geeks, c'est pas qu'on a une case en moins, c'est juste qu'on compte à partir de zéro.
    Plus les choses changent, plus elles restent les mêmes

  10. #10
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 256
    Points
    12 256
    Billets dans le blog
    1
    Par défaut
    Il semble y avoir une erreur dans le code ici:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	$all{$line[5]}{$line[6]}{$line[7]} += $line[24];
    car $line[24] contient le 25ème élément. Si la somme doit se faire sur le champ 24, alors il faut écrire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	$all{$line[5]}{$line[6]}{$line[7]} += $line[23];
    Sauf que le champ 24 est nul dans les données proposées, la somme n'est pas très intéressante. Ci-dessous, je vais utiliser aussi $ligne[24] pour avoir une somme intéressante.

    Je proposerais de simplifier le code en concaténant les champs 6, 7, 8 pour former la clef du hash. Le parcours du hash est ensuite bien plus simple.

    Par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    my %all;
    while (<DATA>) {
    	my @line = split /\s+/;
    	$all{"$line[5] $line[6] $line[7]"} += $line[24]; # devrait être 23
    }
    foreach my $cle (keys %all) {
    	print "$cle $all{$cle} \n";
    }
    __DATA__
    57  55  3 4290281  508  101  209  202  0  10001  7  629 102 0 24   25 3455    0   0  0 1782   0  0     0   1200    AUG 31 2012 11:41AM
    57  55  3 4261131  508  101  209  202  0  10000  7  629 102 0 24   25 2574    0   0  0 7777   0  50679 0   4934    AUG 31 2012 11:41AM
    57  55  3 4642611  508  101  209  201  0  10000  7  629 102 0 24   25 3455    0   0  0 8924   0  50677 0   400     AUG 31 2012 11:41AM
    57  55  3 4991920  508  100  209  202  0  10001  7  629 102 0 24   25 5811    0   0  0 2896   0  0     0   10266   AUG 31 2012 11:41AM

    Avec ces données, ça me donne:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    101 209 201 400
    101 209 202 6134
    100 209 202 10266
    Si je remets les 4 lignes en entrée une seconde fois, j'obtiens:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    101 209 201 800
    101 209 202 12268
    100 209 202 20532

  11. #11
    Membre éprouvé Avatar de Gardyen
    Homme Profil pro
    Bio informaticien
    Inscrit en
    Août 2005
    Messages
    637
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bio informaticien

    Informations forums :
    Inscription : Août 2005
    Messages : 637
    Points : 1 050
    Points
    1 050
    Par défaut
    dans son premier code il utilise les positions 5 6 7 24, donc j'ai repris les mêmes.

    effectivement concaténer les clés est plus simple, j'avais dit que c'était un premier script vite fait
    Nous les geeks, c'est pas qu'on a une case en moins, c'est juste qu'on compte à partir de zéro.
    Plus les choses changent, plus elles restent les mêmes

  12. #12
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 32
    Points : 30
    Points
    30
    Par défaut
    Merci à tous le deux
    C'est Nickel

    Je passe le sujet à résolu
    Encore merci

  13. #13
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 256
    Points
    12 256
    Billets dans le blog
    1
    Par défaut
    Je serais intéressé par une comparaison des performances (durée d'exécution, empreinte mémoire, etc.) entre ma version et celle de Gardyen sur ton fichier de 80.000 lignes, si tu as l'occasion de la faire.

  14. #14
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 32
    Points : 30
    Points
    30
    Par défaut
    Lolo78,

    Alors, pour 800.000 lignes (même fichier que précédemment) :
    - 1m36
    - ~50% d'utilisation sur 1 CPU
    - 270 Mo de RAM utilisé

  15. #15
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 256
    Points
    12 256
    Billets dans le blog
    1
    Par défaut
    Merci d'avoir fait le test. Ma version est donc 25% plus rapide mais consomme deux fois plus de RAM. C'est curieux comme résultat, et intéressant. Du coup je me demande comment fonctionne la fonction de hachage.

    (Je travaille régulièrement sur de très très gros volumes de données, parfois plusieurs centaines de millions d'enregistrements représentant dix ou quinze gigaoctets, ces questions de performance et d'empreinte mémoire ont de l'importance pour moi. Clairement, la stratégie consistant à stocker les données dans un hash en mémoire ne fonctionne pas avec de tels volumes de données, je ne l'utilise donc pas dans de tels cas. Mais quand je suis assez près de la limite mémoire, c'est intéressant de savoir qu'un hash de hash de hash consomme deux fois moins de mémoire qu'un hash ayant les clés concaténées.)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Free Pascal] Détecter les lignes identiques dans deux tableaux de texte
    Par jeanmich dans le forum Free Pascal
    Réponses: 11
    Dernier message: 10/01/2009, 15h43
  2. Réponses: 8
    Dernier message: 28/12/2008, 17h08
  3. ligne identique dans une requête
    Par Soncar dans le forum VB.NET
    Réponses: 2
    Dernier message: 13/10/2008, 16h44
  4. Deux champs identiques dans la même table
    Par amne26 dans le forum Modélisation
    Réponses: 2
    Dernier message: 22/09/2008, 20h29
  5. Supprimer lignes identiques dans un fichier
    Par Gad29 dans le forum Langage
    Réponses: 19
    Dernier message: 30/05/2007, 15h09

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo