web-dev-qa-db-fra.com

Obtenir le nombre de valeurs uniques dans une colonne dans bash

J'ai des fichiers délimités par des tabulations avec plusieurs colonnes. Je souhaite compter la fréquence d'apparition des différentes valeurs d'une colonne pour tous les fichiers d'un dossier et les trier par ordre décroissant de nombre (le nombre le plus élevé en premier). Comment pourrais-je accomplir cela dans un environnement de ligne de commande Linux?

Il peut utiliser n’importe quel langage de ligne de commande courant comme awk, Perl, python etc.).

87
sfactor

Pour voir un nombre de fréquences pour la colonne deux (par exemple):

awk -F '\t' '{print $2}' * | sort | uniq -c | sort -nr

fileA.txt

z    z    a
a    b    c
w    d    e

fichierB.txt

t    r    e
z    d    a
a    g    c

fichierC.txt

z    r    a
v    d    c
a    m    c

Résultat:

  3 d
  2 r
  1 z
  1 m
  1 g
  1 b
140
Dennis Williamson

Voici un moyen de le faire dans la coquille:

FIELD=2
cut -f $FIELD * | sort| uniq -c |sort -nr

C'est le genre de chose que bash est génial.

63
Thedward

Le site GN suggère ce script de Nice awk, qui affiche à la fois les mots et leur fréquence.

Changements possibles:

  • Vous pouvez diriger à travers sort -nr (et inverser Word et freq[Word]) pour voir le résultat en ordre décroissant.
  • Si vous voulez une colonne spécifique, vous pouvez omettre la boucle for et écrire simplement freq[3]++ _ - remplacez 3 par le numéro de colonne.

Voici:

 # wordfreq.awk --- print list of Word frequencies

 {
     $0 = tolower($0)    # remove case distinctions
     # remove punctuation
     gsub(/[^[:alnum:]_[:blank:]]/, "", $0)
     for (i = 1; i <= NF; i++)
         freq[$i]++
 }

 END {
     for (Word in freq)
         printf "%s\t%d\n", Word, freq[Word]
 }
8
Adam Matan

Perl

Ce code calcule les occurrences de toutes colonnes et affiche un rapport trié pour chacune d'entre elles:

# columnvalues.pl
while (<>) {
    @Fields = split /\s+/;
    for $i ( 0 .. $#Fields ) {
        $result[$i]{$Fields[$i]}++
    };
}
for $j ( 0 .. $#result ) {
    print "column $j:\n";
    @values = keys %{$result[$j]};
    @sorted = sort { $result[$j]{$b} <=> $result[$j]{$a}  ||  $a cmp $b } @values;
    for $k ( @sorted ) {
        print " $k $result[$j]{$k}\n"
    }
}

Enregistrez le texte sous columnvalues.pl
Exécutez-le comme: Perl columnvalues.pl files*

Explication

Au niveau supérieur de la boucle while:
* Boucle sur chaque ligne des fichiers d'entrée combinés
* Fractionner la ligne dans le tableau @Fields
* Pour chaque colonne, incrémentez la structure de données du tableau de hachages résultant

Au niveau supérieur de la boucle:
* Boucle sur le tableau de résultats
* Imprimer le numéro de colonne
* Obtenir les valeurs utilisées dans cette colonne
* Trier les valeurs en fonction du nombre d'occurrences
* Tri secondaire basé sur la valeur (par exemple b vs g vs m vs z)
* Parcourez le résultat en utilisant la liste triée
* Imprimer la valeur et le nombre de chaque occurrence

Résultats basés sur les exemples de fichiers fournis par @Dennis

column 0:
 a 3
 z 3
 t 1
 v 1
 w 1
column 1:
 d 3
 r 2
 b 1
 g 1
 m 1
 z 1
column 2:
 c 4
 a 3
 e 2

Entrée .csv

Si vos fichiers d’entrée sont au format .csv, changez /\s+/ à /,/

Obfuscation

Dans un vilain concours, Perl est particulièrement bien équipé.
Ce one-liner fait la même chose:

Perl -lane 'for $i (0..$#F){$g[$i]{$F[$i]}++};END{for $j (0..$#g){print "$j:";for $k (sort{$g[$j]{$b}<=>$g[$j]{$a}||$a cmp $b} keys %{$g[$j]}){print " $k $g[$j]{$k}"}}}' files*
6
Chris Koknat

Rubis (1.9+)

#!/usr/bin/env Ruby
Dir["*"].each do |file|
    h=Hash.new(0)
    open(file).each do |row|
        row.chomp.split("\t").each do |w|
            h[ w ] += 1
        end
    end
    h.sort{|a,b| b[1]<=>a[1] }.each{|x,y| print "#{x}:#{y}\n" }
end
2
kurumi