Abrufen der Anzahl eindeutiger Werte in einer Spalte in Bash


95

Ich habe tabulatorgetrennte Dateien mit mehreren Spalten. Ich möchte die Häufigkeit des Auftretens der verschiedenen Werte in einer Spalte für alle Dateien in einem Ordner zählen und sie in absteigender Reihenfolge sortieren (höchste Anzahl zuerst). Wie würde ich dies in einer Linux-Befehlszeilenumgebung erreichen?

Es kann jede gängige Befehlszeilensprache wie awk, perl, python usw. verwenden.

Antworten:


152

So zeigen Sie eine Frequenzzählung für Spalte zwei an (zum Beispiel):

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

fileA.txt

z    z    a
a    b    c
w    d    e

fileB.txt

t    r    e
z    d    a
a    g    c

fileC.txt

z    r    a
v    d    c
a    m    c

Ergebnis:

  3 d
  2 r
  1 z
  1 m
  1 g
  1 b

68

Hier ist eine Möglichkeit, dies in der Shell zu tun:

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

Dies ist die Art von Dingen, in denen Bash großartig ist.


22
Die "Art" der Sache ... ar ar ar! :)
John Rix

3
Ein bisschen ein einzigartiges Ding. : P (übrigens -d,zum Trennen von Feldern durch Komma oder ein anderes Trennzeichen).
cprn

4
Ich habe benutzt cut -f 1 -d ' '. Vielen Dank. :)
Alfonso Nishikawa

8

Die GNU-Site schlägt dieses nette awk-Skript vor, das sowohl die Wörter als auch ihre Häufigkeit druckt.

Mögliche Änderungen:

  • Sie können durchlaufen sort -nr(und umkehren wordund freq[word]), um das Ergebnis in absteigender Reihenfolge anzuzeigen.
  • Wenn Sie eine bestimmte Spalte möchten, können Sie die for-Schleife weglassen und einfach schreiben freq[3]++- ersetzen Sie 3 durch die Spaltennummer.

Hier geht:

 # 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]
 }

2
Tolles Beispielskript. Es zeigt so viel von der Fähigkeit von awk.
David Mann

Dieses Skript war hilfreich für mich, um festzustellen, auf welche Zeilen in einer Excel-Arbeitsmappe ich wirklich achten musste :) (Excel-Inhalte in Textdatei kopieren, awk verwenden und voila!, Ich kann eine Musterdatei für grep -n erstellen) .
Jubelt

6

Perl

Dieser Code berechnet das Vorkommen aller Spalten und druckt für jede Spalte einen sortierten Bericht:

# 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"
    }
}

Speichern Sie den Text als columnvalues.pl
Führen Sie ihn aus als: perl columnvalues.pl files*

Erläuterung

In der while-Schleife der obersten Ebene:
* Schleife über jede Zeile der kombinierten Eingabedateien
* Teilen Sie die Zeile in das @ Fields-Array auf
* Inkrementieren Sie für jede Spalte die Ergebnis-Array-of-Hash-Datenstruktur

In der for-Schleife der obersten Ebene:
* Schleife über das Ergebnisarray
* Drucken der Spaltennummer
* Abrufen der in dieser Spalte verwendeten Werte
* Sortieren der Werte nach der Anzahl der Vorkommen
* Sekundäre Sortierung basierend auf dem Wert (z. B. b vs g vs m vs z)
* Durchlaufen Sie den Ergebnis-Hash anhand der sortierten Liste
* Drucken Sie den Wert und die Anzahl jedes Vorkommens aus

Ergebnisse basierend auf den von @Dennis bereitgestellten Beispiel-Eingabedateien

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

CSV-Eingabe

Wenn Ihre Eingabedateien .csv sind, wechseln Sie /\s+/zu/,/

Verschleierung

In einem hässlichen Wettbewerb ist Perl besonders gut ausgerüstet.
Dieser Einzeiler macht das Gleiche:

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*

2

Rubin (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

5
Das ist sehr interessant, sowohl weil ich es benutzt habe und es funktioniert hat, als auch weil ich erstaunt bin, wie hässlich Rubin ist. Ich fand Perl schlecht!
Ryansstack

Zur Verteidigung von Ruby könnte dies wirklich zunichte gemacht werden. Zum Beispiel each_with_objectunter anderem mit. Kurz gesagt, dies ist etwas grob geschrieben.
Rambatino
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.