Получение количества уникальных значений в столбце в bash - PullRequest
85 голосов
/ 07 февраля 2011

У меня есть файлы с разделителями табуляции с несколькими столбцами.Я хочу подсчитать частоту появления различных значений в столбце для всех файлов в папке и отсортировать их в порядке убывания количества (сначала самое высокое количество).Как бы я достиг этого в среде командной строки Linux?

Он может использовать любой распространенный язык командной строки, например awk, perl, python и т. Д.

Ответы [ 5 ]

137 голосов
/ 07 февраля 2011

Чтобы просмотреть счетчик частоты для второго столбца (например):

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
59 голосов
/ 07 февраля 2011

Вот способ сделать это в оболочке:

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

Это то, в чем хорош bash.

8 голосов
/ 07 февраля 2011

Сайт GNU предлагает этот красивый скрипт awk, который печатает как слова, так и их частоту.

Возможные изменения:

  • Вы можете передать по sort -nr (и наоборот word и freq[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]
 }
5 голосов
/ 17 сентября 2015

Perl

Этот код вычисляет вхождения всех столбцов и печатает отсортированный отчет для каждого из них:

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

Сохраняет текст как значения столбцов.pl
Запустите его как: perl columnvalues.pl files*

Объяснение

В цикле верхнего уровня while:
* Цикл над каждой строкой объединенных входных файлов
* Разделитьстрока в массив @Fields
* Для каждого столбца увеличивайте структуру данных результирующего массива хэшей

В верхнем уровне цикла:
* Зацикливайтесь на массиве результатов
* Вывести номер столбца
* Получить значения, используемые в этом столбце
* Сортировать значения по количеству вхождений
* Вторичная сортировка по значению (например, 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 input

Если ваши входные файлы имеют формат .csv, измените /\s+/ до /,/

Запутывание

В ужасном соревновании 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*
2 голосов
/ 07 февраля 2011

Рубин (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
...