Rechercher : dans
Par :

Perl hash tri par valeur

Dernière réponse le 6 jan 2009 à 20:54:14 deniss92, le 6 jan 2009 à 14:59:14 
 Signaler ce message aux modérateurs

Bonjour,
Je cherche comment faire un tri par valeur dans une table de hachage. Dans le script ci-dessous, le tri par valeur ne fonctionne pas. Le but recherché est de trier par valeur et par ordre décroissant, afin de limiter l'affichage aux dix nombres les plus importants.

Merci d'avance.

#!/usr/bin/perl

use strict;
use warnings;
use Text::Iconv;
my $converter = Text::Iconv->new("ISO-8859-1", " UTF-8");

my ($sec, $min, $heure, $jour, $mois,$annee, undef, undef, undef) = localtime();
$annee += 1900;
my (@tmp,%fields,$event,$trouve,$sujet,$k,$nombre,%stat);
my $annee_mois='*'.$annee.$mois.'*'; # récupération du critère année et mois
my $motif="SIMPLE";
chdir('/user/stats') || die ("Erreur chdir \n");
open RES,">/var/www/cgi-bin/data/top_ten.txt" or die "E/S : $!\n";
chomp(my @logs = glob($annee_mois));
foreach my $e(@logs){
open FILE,"$e" or warn "E/S : $!\n";
while (<FILE>){
chomp;
if ($_=~ /$motif/){
$_ =~s/[\[\]]/\|/g;
@tmp=split(/\|/,$_);
$event=$tmp[0];
$trouve=$tmp[4];
$sujet=lc($tmp[6]);
$sujet= $converter->convert($sujet);
$sujet=~ s/[\?éèêë]/e/g;
$sujet=~ s/[\%\"]//g;
$sujet=~ s/\b(des|du|de|d' ?|le|la|les|l' ?)//g;#mots vides
$sujet=~ s/(\s+|\t)/ /g;# remplace trop d'espace par un seul espace
$sujet=~ s/^\s+|\s+$//g; #suppression des espaces (debut et fin)
unless ($trouve==0){
$_=join (",",$event,$sujet) if $sujet ne "";
push @{$fields{$sujet}},$event;
}
}
}
}
# comptage de nombre de sujet identique
foreach $sujet (sort keys %fields) {
$nombre = 0;
@tmp = @{$fields{$sujet}};
foreach $event (@tmp) {
$nombre++;
}
push @{$stat{$sujet}},$nombre;
}
# tri par valeur et enregisterment dans le fichier de sortie
foreach $k (sort {$stat{$b} cmp $stat{$a}} keys %stat){
print RES "$k", map {",$_"} @{$stat{$k}},"\n";
}

______________________________FIN

Configuration: Linux Fedora
Firefox 2.0.0.18

Meilleures réponses pour « Perl hash tri par valeur » dans :
.exe n'est pas une application Win32 valide VoirLorsque vous lancez un fichier exécutable, Windows vous affiche un message du type : Nom de l'application n'est pas une application Win32 valide Pour y remédier, il existe plusieurs solutions correspondant à différentes causes : Fichier...
TomTom - Attente d'un signal GPS valide / Signal GPS perdu VoirAttente d'un signal GPS valide Votre GPS TomTom (TomTom One, TomTom Go, TomTom One XL, etc.) n'arrive plus à donner sa position et il affiche l'un des messages suivants : Signal GPS perdu depuis xx minutes Attente d'un signal GPS...
SQL - Tri VoirTri des résultats Il est possible en SQL d'organiser les résultats grâce à la clause ORDER BY. La clause ORDER BY est suivie des mots clés ASC ou DESC, qui précisent respectivement si le tri se fait de manière croissante (par défaut) ou...
Perl - Les fichiers VoirLa notion de filehandle On appelle filehandle (traduisez descripteur de fichier), dans un programme Perl, le nom permettant de manipuler une connexion d'entrée-sortie (les entrées-sorties standards vues précédemment sont connues par les filehandles...
Les variables avec Perl VoirConcept de variable avec Perl Une variable est un objet repéré par son nom, pouvant contenir des données, qui pourront être modifiées lors de l'exécution du programme. Les variables en langage Perl peuvent être de trois...

1

lami20j, le 6 jan 2009 à 15:13:36

Salut,

Dans le script ci-dessous, le tri par valeur ne fonctionne pas
foreach $k (sort {$stat{$b} cmp $stat{$a}} keys %stat){


Ben c'est normal vu que tu le demandes de le faire par clés (keys %stat)

Essaie

foreach $k (sort {$stat{$b} cmp $stat{$a}} values %stat){ 
  print RES "$k", map {",$_"} @{$stat{$k}},"\n"; 
} 

106485010510997108
P.S. Je n'ai pas testé ;-)

Répondre à lami20j

2

deniss92, le 6 jan 2009 à 16:51:17

Bonjour lami20j

Quand je lance le script avec

foreach $k (sort {$stat{$b} <=> $stat{$a}} values %stat){
print RES "$k", map {",$_"} @{$stat{$k}},"\n";
}


j'ai le message d'erreur suivant :

Use of uninitialized value in numeric comparison (<=>) at ./top_ten.pl line 52, <FILE> line 36281.

Lorsque je mets cmp, au cas il considère cela des caractères alphanumériques, il affiche le message d'erreur

Use of uninitialized value in string comparison (cmp) at ./top_ten.pl line 52, <FILE> line 36281.

J'ai fait un test avec le script ci-dessous et ça fonctionne avec le mot clé keys :

#!/usr/bin/perl

%doc = ( "Initiation perl" , 5,
"doc perl" , 10,
"comment ça marche", 15 );
foreach $k (sort {$doc{$b} <=> $doc{$a} }
keys %doc)
{
print "$k $doc{$k}\n";
}

Il ya peut être des problèmes dans mes données.

Répondre à deniss92

3

lami20j, le 6 jan 2009 à 17:11:56

Re,

Peux-tu mettre quelques fichiers logs sur cjoint.com pour faire un test chez moi? 106485010510997108

Répondre à lami20j

5

deniss92, le 6 jan 2009 à 18:35:30

http://cjoint.com/?bgtkXQhaiM

voici le script

#!/usr/bin/perl
# créer les indicateurs d'analyse de mois n-1
use strict;
use warnings;
use Text::Iconv;
my $converter = Text::Iconv->new("ISO-8859-1", " UTF-8");

my ($sec, $min, $heure, $jour, $mois,$annee, undef, undef, undef) = localtime();
$annee += 1900;
my (@tmp,%fields,$event,$trouve,$sujet,$k,$nombre,%stat,@dates);
#my $annee_mois='*'.$annee.$mois.'*'; # car je n'ai pas les données de janvier
my $annee_mois='*200812*';

chdir('/user/stats') || die ("Erreur chdir \n");
open RES,">/var/www/cgi-bin/data/top_ten.txt" or die "E/S : $!\n";
chomp(my @logs = glob($annee_mois));
foreach my $e(@logs){
open FILE,"$e" or warn "E/S : $!\n";
while (<FILE>){
chomp;
if ($_=~ /CONSULT\|link\|SIMPLE\|/){
$_ =~s/[\[\]]/\|/g;
@tmp=split(/\|/,$_);
$event=$tmp[0];
$trouve=$tmp[4];
$sujet=lc($tmp[6]);
$sujet= $converter->convert($sujet);
$sujet=~ s/[\?éèêë]/e/g;
$sujet=~ s/[\%\"]//g;
$sujet=~ s/\b(des|du|de|d' ?|le|la|les|l' ?)//g;#mots vides
$sujet=~ s/(\s+|\t)/ /g;# remplace trop d'espace par espace
$sujet=~ s/^\s+|\s+$//g; #suppression des espaces (debut et fin)
unless ($trouve==0){
$_=join (",",$event,$sujet) if $sujet ne "";
push @{$fields{$sujet}},$event;
}
}
}
}
foreach $sujet (sort keys %fields) {
$nombre = 0;
@dates = @{$fields{$sujet}};
foreach $event (@dates) {
$nombre++;
}
$sujet="\"".$sujet."\"";
push @{$stat{$sujet}},$nombre;
}
foreach $k (sort {$stat{$b} <=> $stat{$a}} values %stat){
print RES "$k", map {",$_"} @{$stat{$k}},"\n";
}

Répondre à deniss92

4

deniss92, le 6 jan 2009 à 17:35:24

Ok,

Je vais les déposer. Je dépose aussi le script tel que, car je l'ai légèrement modifier ici.

A bientot

Merci

Répondre à deniss92

6

lami20j, le 6 jan 2009 à 20:05:45

Re,

Je pense qu'il sera plus facile si tu me dis quel champ tu veux récupérer et compter.

Déjà je vois que seulement les lignes qui content |CONSULT|link|SIMPLE| t'intéresse
Je vois aussi qu'une séparation est faite avec |

Par exemple sur celle ligne

20081211123923|CONSULT|link|SIMPLE|260| PERSONNES.DPERS_DESYN and [de gaulle%] bool_and THES_GEO.DTHGEO_DESYN and [de gaulle%] bool_or THES_RAMEAU.DTHRAM_DESYN and [de gaulle%] bool_or NOTICES.DNOT_MOTS and


Quelle champ tu comptes? 106485010510997108

Répondre à lami20j

7

deniss92, le 6 jan 2009 à 20:13:24

Je compte le premier champ entre crochet. Par exemple [de gaulle]. Et je ne prends pas les autres champs entre crochet, c'est la même chose tout le temps pour une requête. Par ailleurs, je ne compte que si je champ qui suit le champ SIMPLE est supérieur à 0. Par exemple SIMPLE|260|

Répondre à deniss92

8

lami20j, le 6 jan 2009 à 20:16:58

Re,

Si je comprends bien tu compte 3 fois [de gaulle]

ensuite tu as 3 fois [france allemagne]
20081211015850|CONSULT|link|SIMPLE|397| PERSONNES.DPERS_DESYN and [france allemagne%] bool_and THES_GEO.DTHGEO_DESYN and [france allemagne%] bool_or THES_RAMEAU.DTHRAM_DESYN and [france allemagne%] bool_or N

et ainsi de suite et la fin de veux affiche les 10 groupes avec un nombre d'apparitions plus grandes?
106485010510997108

Répondre à lami20j

9

deniss92, le 6 jan 2009 à 20:20:18

C'est bien ça. C'est pour faire un top ten.

Je ne compte qu'une fois par date. Le premier champ représente la date (et heure+minute..)

Répondre à deniss92

10

lami20j, le 6 jan 2009 à 20:24:36

Re,

Je ne compte qu'une fois par date.
La phrase qui tue. Je ne comprends pas. 106485010510997108

Répondre à lami20j

11

lami20j, le 6 jan 2009 à 20:27:37

Re,

Ca veut dire que par exemple ici

20081230221213|CONSULT|link|SIMPLE|293| PERSONNES.DPERS_DESYN and ["historique" "REGIMENT%"] bool_and THES_GEO.DTHGEO_DESYN and ["historique" "REGIMENT%"] bool_or THES_RAMEAU.DTHRAM_DESYN and ["historique" "
20081230221331|CONSULT|link|SIMPLE|293| PERSONNES.DPERS_DESYN and ["historique" "REGIMENT%"] bool_and THES_GEO.DTHGEO_DESYN and ["historique" "REGIMENT%"] bool_or THES_RAMEAU.DTHRAM_DESYN and ["historique" "

Tu vas compter une seule fois ["historique" "REGIMENT%"] (la date est la même, c'est l'heure qui est différente)?

106485010510997108

Répondre à lami20j

12

deniss92, le 6 jan 2009 à 20:33:23

Prenons l'exemple suivant :

20081211015850|CONSULT|link|SIMPLE|397| PERSONNES.DPERS_DESYN and [france allemagne%] bool_and THES_GEO.DTHGEO_DESYN and [france allemagne%] bool_or THES_RAMEAU.DTHRAM_DESYN and [france allemagne%] bool_or N

Le premier champ est 20081211015850, il faut lire annee 2008, 12 pour décembre, 11 pour le jour et le reste représente l'heure, minute, seconde. Une nouvelle ne commence qu'avec une date. Par conséquent, l'exemple ci-dessus est une requete donc, je ne compte [de gaulle%] qu'une fois. Dans mon script, c'est le fields[6], après avoir remplacer les "[ ]" par "|"

Répondre à deniss92

13

lami20j, le 6 jan 2009 à 20:38:14

Re,

Ok, donc le champ tu le compte une seule fois pas 3 d'accord.

Pour les dates j'ai bien compris.

Ma question est : Si tu as deux lignes avec la même date (je ne parle pas des heures, min ,sec) tu comptes toujours ou pas?
106485010510997108

Répondre à lami20j

14

deniss92, le 6 jan 2009 à 20:41:29

Les utilisateurs se connectent avec le même compte. Autrement dit, en 1 heure une dizaine d'utilsateur peuvent se connecte avec le même compte. Donc il faut prendre en compte les heures. Par conséquent, il compter même quand l'heure change. Si toi et moi, on le compte toto, je lance une requête 20h10 et toi a 20h20, il faut compter deux requêtes.

Répondre à deniss92

15

lami20j, le 6 jan 2009 à 20:44:00

Re,

Ben, je ne comprends pas ;-(( 106485010510997108

Répondre à lami20j

16

 deniss92, le 6 jan 2009 à 20:54:14

Dans l'exemple ci-dessous (qui est rare et je suis d'accord avec toi).

20081230221213|CONSULT|link|SIMPLE|293| PERSONNES.DPERS_DESYN and ["historique" "REGIMENT%"] bool_and THES_GEO.DTHGEO_DESYN and ["historique" "REGIMENT%"] bool_or THES_RAMEAU.DTHRAM_DESYN and ["historique" "
20081230221331|CONSULT|link|SIMPLE|293| PERSONNES.DPERS_DESYN and ["historique" "REGIMENT%"] bool_and THES_GEO.DTHGEO_DESYN and ["historique" "REGIMENT%"] bool_or THES_RAMEAU.DTHRAM_DESYN and ["historique" "

Pourquoi le meme utilisateur, relancerait-il la meme requête en 2 minutes à peine ? car la valeur 293 signifie qu'il à trouvé quelques choses !

Répondre à deniss92
Collection CommentÇaMarche.net