Получение количества уникальных значений в столбце в bash
У меня есть файлы с разделителями табуляции с несколькими столбцами. Я хочу рассчитать частоту появления разных значений в столбце для всех файлов в папке и отсортировать их в порядке убывания количества (сначала подсчитывать счетчик). Как я могу выполнить это в среде командной строки Linux?
Он может использовать любой обычный язык командной строки, такой как awk, perl, python и т.д.
Ответы
Ответ 1
Чтобы увидеть частоту отсчета для столбца два (например):
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
Ответ 2
Вот как это сделать в оболочке:
FIELD=2
cut -f $FIELD * | sort| uniq -c |sort -nr
Это что-то вроде bash отлично.
Ответ 3
Сайт GNU предлагает этот приятный awk script, который печатает как слова, так и их частоту.
Возможные изменения:
- Вы можете пропустить через
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]
}
Ответ 4
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"
}
}
Сохраните текст как columnvalues.pl
Запустите его как: perl columnvalues.pl files*
Описание
В цикле верхнего уровня:
* Цикл по каждой строке комбинированных входных файлов
* Разделить строку на массив @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+/
на /,/
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*
Ответ 5
Рубин (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