Mendapatkan hitungan nilai unik dalam kolom di bash

95

Saya memiliki file tab delimited dengan beberapa kolom. Saya ingin menghitung frekuensi kemunculan nilai yang berbeda dalam kolom untuk semua file dalam folder dan mengurutkannya dalam urutan penghitungan yang menurun (jumlah tertinggi terlebih dahulu). Bagaimana saya melakukannya di lingkungan baris perintah Linux?

Itu dapat menggunakan bahasa baris perintah umum seperti awk, perl, python dll.

sfactor
sumber

Jawaban:

153

Untuk melihat jumlah frekuensi untuk kolom dua (misalnya):

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

Hasil:

  3 d
  2 r
  1 z
  1 m
  1 g
  1 b
Dijeda sampai pemberitahuan lebih lanjut.
sumber
68

Berikut cara melakukannya di shell:

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

Ini adalah jenis hal yang hebat dari pesta.

Maju
sumber
23
"Semacam" hal ... ar ar ar! :)
John Rix
3
Agak unik. : P (btw. Gunakan -d,untuk membatasi bidang dengan koma atau pembatas lainnya).
cprn
4
Saya dulu cut -f 1 -d ' '. Terima kasih banyak. :)
Alfonso Nishikawa
8

Situs GNU menyarankan skrip awk yang bagus ini, yang mencetak kata dan frekuensinya.

Perubahan yang mungkin terjadi:

  • Anda dapat menyalurkan sort -nr(dan membalikkan worddan freq[word]) untuk melihat hasilnya dalam urutan menurun.
  • Jika Anda menginginkan kolom tertentu, Anda dapat menghilangkan loop for dan cukup menulis freq[3]++- ganti 3 dengan nomor kolom.

Ini dia:

 # 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]
 }
Adam Matan
sumber
2
Skrip contoh yang bagus. Ini menunjukkan begitu banyak kemampuan awk.
David Mann
Skrip ini sangat membantu saya untuk menentukan baris mana di buku kerja Excel yang benar-benar perlu saya perhatikan :) (menyalin konten Excel ke file teks, gunakan awk, dan, voila !, saya bisa membuat file pola untuk grep -n) .
Jubbles
6

Perl

Kode ini menghitung kemunculan semua kolom, dan mencetak laporan yang diurutkan untuk masing-masing:

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

Simpan teks sebagai columnvalues.pl
Jalankan sebagai: perl columnvalues.pl files*

Penjelasan

Di level teratas while loop:
* Ulangi setiap baris dari file input gabungan
* Pisahkan baris menjadi array @Fields
* Untuk setiap kolom, tambahkan struktur data array-of-hashes hasil

Di tingkat teratas untuk perulangan:
* Ulangi larik hasil
* Cetak nomor kolom
* Dapatkan nilai yang digunakan dalam kolom itu
* Urutkan nilai berdasarkan jumlah kejadian
* Urutan sekunder berdasarkan nilai (misalnya b vs g vs m vs z)
* Iterasi melalui hash hasil, menggunakan daftar yang diurutkan
* Cetak nilai dan jumlah setiap kemunculan

Hasil berdasarkan file input sampel yang disediakan oleh @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

masukan .csv

Jika file masukan Anda adalah .csv, ubah /\s+/ke/,/

Obfuscation

Dalam kontes yang buruk, Perl dilengkapi dengan sangat baik.
Satu baris ini melakukan hal yang sama:

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*
Chris Koknat
sumber
2

Ruby (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
kurumi
sumber
5
Ini sangat menarik, baik karena saya menggunakannya dan berhasil, dan juga karena saya kagum dengan betapa jeleknya ruby ​​.. Saya pikir perl itu buruk!
ryansstack
Dalam pertahanan Ruby, ini bisa benar-benar rapi. Misalnya, menggunakan each_with_object, antara lain. Singkatnya, ini ditulis dengan kasar.
Rambatino