Подсчет экземпляров мотива для групп с использованием awk - PullRequest
0 голосов
/ 16 января 2019

У меня есть файл, размер которого превышает 20 ГБ (тонны и тонны строк). Строки выглядят примерно так:

group1 motifA
group1 motifD
group1 motifD
group1 motifRalph
group1 motifA
group2 motifDenzel
group2 motifRodger
group2 motifHector
group2 motifDenzel
group2 motifA
group2 motifXYZ
group2 motifA
group2 motifDenzel
group3 motifHector
group3 motifRalph
group4 motifA
...
group10389

Я хочу создать сводный файл, в котором перечислены мотивы, которые появляются в каждой группе и сколько раз. Например:

group1 motifA 2
group1 motifD 2
group1 motifRalph 1
group2 motifDenzel 3
group2 motifRodger 1
group2 motifHector 1

Но я хочу это для всех мотивов. Я знаю, что с помощью awk вы можете использовать количество таких как:

awk '{count[$1]++} END

Но как это сделать для подсчета групп, как я описал? Очень признателен!

Ответы [ 6 ]

0 голосов
/ 16 января 2019

Другое решение, использующее GNU datamash :

 $ datamash -t ' ' groupby 1,2 count 2 < input.txt
group1 motifA 1
group1 motifD 2
group1 motifRalph 1
group1 motifA 1
group2 motifDenzel 1
group2 motifRodger 1
group2 motifHector 1
group2 motifDenzel 1
group2 motifA 1
group2 motifXYZ 1
group2 motifA 1
group2 motifDenzel 1
group3 motifHector 1
group3 motifRalph 1
group4 motifA 1

(Предполагается, что ваш входной файл уже отсортирован по двум полям; если не добавить -s; не знаю, как это 'я буду работать с 20-гигабайтным файлом, хотя)

0 голосов
/ 16 января 2019

Вы также можете попробовать Perl

$  perl -lane ' $kv{$_}++;END { foreach(keys %kv) { print "$_ $kv{$_}" } } ' steveman.txt
group3 motifHector 1
group2 motifXYZ 1
group2 motifDenzel 3
group1 motifD 2
group2 motifA 2
group1 motifRalph 1
group3 motifRalph 1
group1 motifA 2
group4 motifA 1
group2 motifRodger 1
group2 motifHector 1
$
0 голосов
/ 16 января 2019

Вы упоминаете, что у вас есть файл размером 20 ГБ, с потенциально большим количеством разных строк. В худшем случае каждая строка отличается. Это означает, что вам понадобится 20 ГБ памяти, если вы хотите сделать что-то подобное.

awk '{a[$0]++}END{for (i in a) print i, a[i] }'

Так что этот подход не очень полезен. Исходя из вашего ввода, похоже, ваш файл отсортирован по первому столбцу. В этом случае может помочь следующее:

awk '($1 != key) { for(i in a) print i, a[i]; delete a }
     {a[$0]++; key = $1}
     END { for(i in a) print i, a[i] }' file

Но, опять же, это может работать не так, как ожидалось, поскольку файл может содержать только одну группу, и снова вам может потребоваться 20 ГБ памяти.

Лучший подход - использовать sort и uniq. Сортировка GNU разработана так, что она может легко обрабатывать файлы, размер которых намного превышает общий объем памяти в вашей системе. Однако вам может потребоваться добавить несколько дополнительных параметров:

--temporary-directory=tempdir: Используйте каталог tempdir для хранения временных файлов, переопределяя переменную окружения TMPDIR. Если этот параметр указан несколько раз, временные файлы сохраняются во всех указанных каталогах. Если у вас большая сортировка или слияние с привязкой к вводу / выводу, вы часто можете повысить производительность, используя этот параметр для указания каталогов на разных дисках и контроллерах.

Эта опция может потребоваться, так как /tmp может не вместить достаточно дискового пространства для сортировки 20 ГБ данных

источник: GNU Coreutils Sort Invocation

sort --temporary-directory=/home/user/tempdir bigfile | uniq -c

Чтобы определить, какой подход вам нужно использовать, я бы предложил:

  1. Сначала подсчитайте общее количество записей в группе:

    $ cut -d " " -f1 file | uniq -c | sort -n | awk '(NR==1)END{print $0}'
    

    В приведенной выше строке будет напечатана самая маленькая и самая большая группа (при условии, что ваш файл отсортирован по группе)

  2. Получите максимальную длину строки:

    $ awk { l=length($0); m=m<l?l:m } END {print m}' file
    
  3. Если n_max (самая большая группа) раз l_max (самая большая длина) порядка вашего общего объема памяти, используйте опцию sort

  4. В противном случае используйте второй вариант.

  5. Никогда не используйте первую опцию для больших файлов.

0 голосов
/ 16 января 2019

если ваши группы сгруппированы, этот подход должен работать лучше всего.

$ awk 'function pr() {for(k in a) print p,k,a[k]} 
       p!=$1 {pr(); delete a; p=$1} 
             {a[$2]++} 
       END   {pr()}' file

group1 motifA 2
group1 motifD 2
group1 motifRalph 1
group2 motifA 2
group2 motifHector 1
group2 motifDenzel 3
group2 motifXYZ 1
group2 motifRodger 1
group3 motifHector 1
group3 motifRalph 1
group4 motifA 1

сканирует файл только один раз, не нуждается в сортированном вводе и сохраняет в памяти только один групповой блок уникальных полей.

0 голосов
/ 16 января 2019

не тестируется с файлом 20 Гб, но обычно вы можете сделать с:

sort inputfile |uniq -c

  2 group1 motifA
  2 group1 motifD
  1 group1 motifRalph
  2 group2 motifA
  3 group2 motifDenzel
  1 group2 motifHector
  1 group2 motifRodger
  1 group2 motifXYZ
  1 group3 motifHector
  1 group3 motifRalph
  1 group4 motifA
  ......
0 голосов
/ 16 января 2019

Не могли бы вы попробовать следующее.Чтение Input_file 2 раза здесь.

awk 'FNR==NR{a[$1,$2]++;next} a[$1,$2]{print $0,a[$1,$2];delete a[$1,$2]}' Input_file Input_file

2-й подход: Чтение Input_file только 1 раз (Это может занять некоторое время, согласно комментарию Tiw в разделе комментариев свесь Input_file загружается в память или около того).

awk '!a[$1 FS $2]++{b[++count]=$1 OFS $2;} {c[$1 FS $2]++}  END{for(i=1;i<=count;i++){print b[i],c[b[i]]}}'  Input_file
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...