web-dev-qa-db-ja.com

bashの列の一意の値の数を取得する

複数の列を持つタブ区切りファイルがあります。フォルダー内のすべてのファイルについて、列内のさまざまな値の出現頻度をカウントし、カウントの降順で並べ替えます(最も高いカウントが最初になります)。 Linuxコマンドライン環境でこれをどのように実現しますか?

Awk、Perl、pythonなどの一般的なコマンドライン言語を使用できます。

87
sfactor

列2の頻度カウントを表示するには(たとえば):

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

結果:

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

シェルでそれを行う方法は次のとおりです。

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

これは、bashの優れた点です。

63
Thedward

GNUサイト は、このNice awkスクリプトを示唆しています。これは、単語とその頻度の両方を出力します。

可能な変更:

  • sort -nr(およびWordfreq[Word]を逆に)でパイプ処理して、降順で結果を確認できます。
  • 特定の列が必要な場合は、forループを省略して、単にfreq[3]++と書くことができます-3を列番号に置き換えます。

ここに行く:

 # 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

このコードは、all列の出現を計算し、各列のソートされたレポートを出力します。

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

テキストをcolumnvalues.plとして保存します
実行:Perl columnvalues.pl files*

説明

トップレベルのwhileループで:
*結合された入力ファイルの各行をループします
*行を@Fields配列に分割します
*すべての列について、結果のハッシュ配列データ構造をインクリメントします

最上位のforループで:
*結果配列をループします
*列番号を出力します
*その列で使用されている値を取得する
*発生回数で値をソートします
*値に基づく2次ソート(たとえば、b vs g vs m vs z)
*ソートされたリストを使用して、結果ハッシュを反復処理します
*各出現の値と数を出力します

@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

.csv入力

入力ファイルが.csvの場合、/\s+//,/に変更します

Obfuscation

いコンテストでは、Perlは特によく装備されています。
このワンライナーは同じことを行います。

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

ルビー(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