Forum Programmation.shell Équivalent tableau croisé dynamique

Posté par . Licence CC by-sa
Tags :
2
2
mai
2014

Bonjour à tous,

Dans le cadre de l'analyse de log, j'ai un fichier de +850000 lignes avec plusieurs colonnes.

Grosso modo, je l'ai réduit à ceci :

Pierre pierre.monsite.com 45
Paul paul.paulsite.com 10
Jacques jacques.sonsite.com 10
Pierre pierre.monsite.com 10
Jacques jacques.sonsite.com 0
Paul paul.paulsite.com 10
Toto toto.coco.com 1

J'aimerai pouvoir faire un tableau croisé dynamique : donc pouvoir sommer les utilisateurs pour n'avoir plus que
Pierre pierre.monsite.com 50
Paul paul.paulsite.com 20
etc…

Je l'ai fait avec des grep et des awks avec un fichier temporaire, mais c'est très lent et sans doute affreusement bateau.

De votre expérience pour ce genre de démarches, qu'utilisez-vous de rapide et si possible dispo de base dans les distrib serveurs ?

Merci pour vos trucs et astuces !

Ps : ce serait encore plus fort si ça gérait un nombre x de colonne :)
```

  • # un awk tout bête ?

    Posté par . Évalué à 3.

    Hello,

    Sur le petit échantillon que tu donnes, le code qui suit me paraît assez rapide ; il faudrait voir ce que ça donne sur sur tes 850 000 lignes.

    #!/usr/bin/awk -f
    
    { tableau[ $1, " ", $2 ] += $3 }
    
    END {for (index in tableau)
           print index, tableau[index]
    }

    Ps : ce serait encore plus fort si ça gérait un nombre x de colonne :)

    Lapin compris cette partie-là ; tu veux dire que ton fichier en entrée n'a pas toujours un nombre fixé de colonnes ?

    • [^] # Re: un awk tout bête ?

      Posté par . Évalué à 1.

      Merci pour la piste, je ne maîtrise pas assez awk, comment puis je passer mon fichier dans ce script ?

      J'ai fait ceci :
      awk -f script-tri.awk < sorted.txt
      awk: script-tri.awk:5: END {for (index in tableau)
      awk: script-tri.awk:5: ^ syntax error
      awk: script-tri.awk:6: print index, tableau[index]
      awk: script-tri.awk:6: ^ syntax error
      awk: script-tri.awk:6: fatal: invalid subscript expression

      Il y a bien un nombre constant de colonne, du coup je pourrais faire le tri sur d'autres champs (nom mentionné ici).

      J'avoue que si cela fonctionne comme ça, ça me parait lisible :)

      • [^] # Re: un awk tout bête ?

        Posté par . Évalué à 2.

        Normalement, chmod +x script-tri.awk et ./script-tri.awk sorted.txt devrait suffire.

      • [^] # Re: un awk tout bête ?

        Posté par . Évalué à 2.

        Le mot « index » n'est pas utilisable comme nom de variable car c'est déjà un nom de fonction interne à awk. Remplace-le par « indice » et ça devrait fonctionner. Je ne sais pas comment Nicolas Casanova a fait pour le faire tourner tel quel.

        • [^] # Re: un awk tout bête ?

          Posté par . Évalué à 1. Dernière modification le 05/05/14 à 12:09.

          Bien joué, ça fonctionne !

          C'est même tellement rapide que je suis étourdi :) -> je vais comparer à Excel et ses tableaux croisés dynamique !

          Par contre, ça me donne une drôle de sortie : avec un fichier de log :

          62.161.248.217^\ ^\e-mailing.power.fr 140112
          46.105.180.51^\ ^\p0051.affinitead.net 111883

          C'est un comportement normal ?

          • [^] # Re: un awk tout bête ?

            Posté par . Évalué à 1.

            62.161.248.217^\ ^\e-mailing.power.fr 140112
            ```Sorry, voici le retour non interprété..
            
            • [^] # Re: un awk tout bête ?

              Posté par . Évalué à 2. Dernière modification le 05/05/14 à 11:43.

              Cela ressemble aux caractères spéciaux utilisés par awk comme séparateur de champs. Dans le script, remplace simplement
              $1, " ", $2
              par
              $1" "$2 pour séparer avec des espaces
              ou
              $1","$2 pour séparer avec des virgules

              • [^] # Re: un awk tout bête ?

                Posté par . Évalué à 1.

                Merci, j'ai tout mis en une ligne pour avoir le top 5:

                awk -F, '{array[$5" "$6]+=$13} END { for (i in array) {print i" " array[i]}}' virus.csv | sort -nr -k3 | head -n5

                Par contre, là je peux faire la somme pour une colonne, mais est-il possible de faire les sommes sur plusieurs colonnes ?

                Begin Timestamp,End Timestamp,Begin Date,End Date,Sender IP Address,Hostname,DNS Verified,SBRS,Last Sender Group,Total Attempted,Stopped by Reputation Filtering,Stopped as Invalid Recipients,Spam Detected,Virus Detected,Stopped by Content Filter,Total Threat,Marketing,Clean
                1393628400.0,1393714799.0,2014-02-28 23:00 GMT,2014-03-01 22:59 GMT,1.53.230.109,unknown domain,No,--,ALL,1,0,0,1,0,0,1,0,0

                Du coup, je pourrais avoir un fichier avec l'IP comme index et avoir les Spam detected, Virus Detected et Total Threat en une seule fois ?

                Pour info, le fichier virus.csv fait 122 mo et 856 000 ligne et est traité en environ 17s juste pour une colonne.

                • [^] # Re: un awk tout bête ?

                  Posté par . Évalué à 2.

                  tout ce joue là

                  {array[$5" "$6]+=$13}

                  tu dois bien pouvoir faire

                  {array["spam "$5" "$6]+=$13;array["virus "$5" "$6]+=$14;}

                  ou carrement un tableau à 2 entrées

                  {array["spam"][$5" "$6]+=$13;array["virus"][$5" "$6]+=$14;}

                  Attention ma syntaxe awk est imaginaire, la separation par ; et le tableau à 2 entrées sont des suppositions de ma part.

                  • [^] # Re: un awk tout bête ?

                    Posté par . Évalué à 2.

                    autant faire deux tableaux dans ce cas: array_spam[…] et array_virus[…]

            • [^] # Re: un awk tout bête ?

              Posté par . Évalué à 2.

              c'est corrigé

              pour copier/coller du code, shell, perl ou autre il faut taper 3 ` suivi du langage, ca donne :

              ```sh
              #!/bin/sh
              moncode shell
              avec
              ses
              fonctions
              etc
              etc
              ```

              quand tu le tapes
              et ca :

              #!/bin/sh
              moncode shell
              avec
              ses
              fonctions
              etc
              etc

              quand tu l'affiches.
              et du coup ca gere les caracteres speciaux
              attention il faut mettre un retour à la ligne avec les 3 `

  • # table de hash perl

    Posté par . Évalué à 3.

    un tableau de hashage perl avec la premiere colonne en clé et la 3e colonne en valeur
    avec la valeur qui s'additionne à la precedente

  • # Perl

    Posté par . Évalué à 1.

    Pour perl, je ne connais pas assez, mais dans un site, il est noté :

    "Vous l'avez sans doute compris, dans une table de hachage, une clef n'est présente qu'une seule fois et ne peut donc avoir qu'une seule valeur (comme l'élément d'un indice donné d'un tableau). Par contre, une valeur peut être associée à plusieurs clefs. "

    Du coup, là j'ai plusieurs clés avec plusieurs valeur (on retrouve Pierre plusieurs fois avec plusieurs valeurs), mais peut-être ai-je mal compris ?

    • [^] # Re: Perl

      Posté par (page perso) . Évalué à 2.

      Oui (encore que ça dépend des langages), mais la table est mutable :

      Si la clef n'est pas présente : tu insères dans ta table le couple (clef,valeur), et si la clef est déjà présente, tu met à jour ta table avec le couple (clef,valeur_actuelle+nouvelle_valeur).

    • [^] # Re: Perl

      Posté par . Évalué à 4.

      c'est exactement ca

      mais "pierre" sera la clef, tu auras une seule "pierre" dans ton tableau
      simplement à chaque fois que tu vois "pierre", il faudra faire un truc ressemblant à çà (attention ce n'est pas correct au niveau syntaxique perl)

      tableau['pierre']+=valeur;

      ou

      tableau['pierre']=valeur + tableau['pierre'];

      qui remplace donc la valeur courante (tableau['pierre'] à droite du =)
      par la nouvelle valeur additionnée de la precedente.

      quand tu as parcouru tout ton fichier de log,
      tu as donc un tableau avec une ligne pour pierre, une ligne pour jean,
      et une valeur tableau['pierre'] qui sera le total des lignes recontrées.

      • [^] # Re: Perl

        Posté par . Évalué à 1.

        Merci, je ne connaissais pas, je vais creuser aussi de ce côté.

        Merci pour vos astuces, pour l'instant je prends la version awk, directement utilisable.

        Bonne soirée à tous !

  • # SGBD

    Posté par (page perso) . Évalué à 1. Dernière modification le 03/05/14 à 20:06.

    Si tu en as un d'installé sur ta machine (MySQL, PostgreSQL ou MariaDB par exemple), charge ton CSV dans une table et fait une requête de ce genre :

    SELECT nom, adresse, SUM(nb) FROM table GROUP BY nom, adresse

    • [^] # Re: SGBD

      Posté par . Évalué à 2.

      Effectivement, c'est l'une des solutions que je voulais tester, mais par exemple sur pas mal d'appliances (dans ce cas un serveur mail Cisco) on trouve awk ou Perl mais pas une DB utilisable.

      Beaucoup de suggestions intéressantes en tout cas, merci à tous.
      ```

      • [^] # Re: SGBD

        Posté par . Évalué à 4.

        Même sqlite ?

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.