как подсчитать частоту букв - PullRequest
0 голосов
/ 15 февраля 2019

У меня есть такие данные

>sp|Q96A73|P33MX_HUMAN Putative monooxygenase p33MONOX OS=Homo sapiens OX=9606 GN=KIAA1191 PE=1 SV=1
RNDDDDTSVCLGTRQCSWFAGCTNRTWNSSAVPLIGLPNTQDYKWVDRNSGLTWSGNDTCLYSCQNQTKGLLYQLFRNLFCSYGLTEAHGKWRCADASITNDKGHDGHRTPTWWLTGSNLTLSVNNSGLFFLCGNGVYKGFPPKWSGRCGLGYLVPSLTRYLTLNASQITNLRSFIHKVTPHR
>sp|P13674|P4HA1_HUMAN Prolyl 4-hydroxylase subunit alpha-1 OS=Homo sapiens OX=9606 GN=P4HA1 PE=1 SV=2
VECCPNCRGTGMQIRIHQIGPGMVQQIQSVCMECQGHGERISPKDRCKSCNGRKIVREKKILEVHIDKGMKDGQKITFHGEGDQEPGLEPGDIIIVLDQKDHAVFTRRGEDLFMCMDIQLVEALCGFQKPISTLDNRTIVITSHPGQIVKHGDIKCVLNEGMPIYRRPYEKGRLIIEFKVNFPENGFLSPDKLSLLEKLLPERKEVEE
>sp|Q7Z4N8|P4HA3_HUMAN Prolyl 4-hydroxylase subunit alpha-3 OS=Homo sapiens OX=9606 GN=P4HA3 PE=1 SV=1
MTEQMTLRGTLKGHNGWVTQIATTPQFPDMILSASRDKTIIMWKLTRDETNYGIPQRALRGHSHFVSDVVISSDGQFALSGSWDGTLRLWDLTTGTTTRRFVGHTKDVLSVAFSSDNRQIVSGSRDKTIKLWNTLGVCKYTVQDESHSEWVSCVRFSPNSSNPIIVSCGWDKLVKVWNLANCKLK
>sp|P04637|P53_HUMAN Cellular tumor antigen p53 OS=Homo sapiens OX=9606 GN=TP53 PE=1 SV=4
IQVVSRCRLRHTEVLPAEEENDSLGADGTHGAGAMESAAGVLIKLFCVHTKALQDVQIRFQPQL
>sp|P10144|GRAB_HUMAN Granzyme B OS=Homo sapiens OX=9606 GN=GZMB PE=1 SV=2
MQPILLLLAFLLLPRADAGEIIGGHEAKPHSRPYMAYLMIWDQKSLKRCGGFLIRDDFVLTAAHCWGSSINVTLGAHNIKEQEPTQQFIPVKRPIPHPAYNPKNFSNDIMLLQLERKAKRTRAVQPLRLPSNKAQVKPGQTCSVAGWGQTAPLGKHSHTLQEVKMTVQEDRKCES
>sp|Q9UHX1|PUF60_HUMAN Poly(U)-binding-splicing factor PUF60 OS=Homo sapiens OX=9606 GN=PUF60 PE=1 SV=1
MGKDYYQTLGLARGASDEEIKRAYRRQALRYHPDKNKEPGAEEKFKEIAEAYDVLSDPRKREIFDRYGEEGLKGSGPSGGSGGGANGTSFSYTFHGDPHAMFAEFFGGRNPFDTFFGQRNGEEGMDIDDPFSGFPMGMGGFTNVNFGRSRSAQEPARKKQDPPVTHDLRVSLEEIYSGCTKKMKISHK
>sp|Q06416|P5F1B_HUMAN Putative POU domain, class 5, transcription factor 1B OS=Homo sapiens OX=9606 GN=POU5F1B PE=5 SV=2
IVVKGHSTCLSEGALSPDGTVLATASHDGYVKFWQIYIEGQDEPRCLHEWKPHDGRPLSCLLFCDNHKKQDPDVPFWRFLITGADQNRELKMWCTVSWTCLQTIRFSPDIFSSVSVPPSLKVCLDLSAEYLILSDVQRKVLYVMELLQNQEEGHACFSSISEFLLTHPVLSFGIQVVSRCRLRHTEVLPAEEENDSLGADGTHGAGAMESAAGVLIKLFCVHTKALQDVQIRFQPQLNPDVVAPLPTHTAHEDFTFGESRPELGSEGLGSAAHGSQPDLRRIVELPAPADFLSLSSETKPKLMTPDAFMTPSASLQQITASPSSSSSGSSSSSSSSSSSLTAVSAMSSTSAVDPSLTRPPEELTLSPKLQLDGSLTMSSSGSLQASPRGLLPGLLPAPADKLTPKGPGQVPTATSALSLELQEVEP
>sp|O14683|P5I11_HUMAN Tumor protein p53-inducible protein 11 OS=Homo sapiens OX=9606 GN=TP53I11 PE=1 SV=2
MIHNYMEHLERTKLHQLSGSDQLESTAHSRIRKERPISLGIFPLPAGDGLLTPDAQKGGETPGSEQWKFQELSQPRSHTSLKVSNSPEPQKAVEQEDELSDVSQGGSKATTPASTANSDVATIPTDTPLKEENEGFVKVTDAPNKSEISKHIEVQVAQETRNVSTGSAENEEKSEVQAIIESTPELDMDKDLSGYKGSSTPTKGIENKAFDRNTESLFEELSSAGSGLIGDVDEGADLLGMGREVENLILENTQLLETKNALNIVKNDLIAKVDELTCEKDVLQGELEAVKQAKLKLEEKNRELEEELRKARAEAEDARQKAKDDDDSDIPTAQRKRFTRVEMARVLMERNQYKERLMELQEAVRWTEMIRASRENPAMQEKKRSSIWQFFSRLFSSSSNTTKKPEPPVNLKYNAPTSHVTPSVK

Я хочу подсчитать, сколько там каждой буквы, поэтому если у меня есть такая, я считаю вот так

>sp|Q96A73|P33MX_HUMAN Putative monooxygenase p33MONOX OS=Homo sapiens OX=9606 GN=KIAA1191 PE=1 SV=1
RNDDDDTSVCLGTRQCSWFAGCTNRTWNSSAVPLIGLPNTQDYKWVDRNSGLTWSGNDTCLYSCQNQTKGLLYQLFRNLFCSYGLTEAHGKWRCADASITNDKGHDGHRTPTWWLTGSNLTLSVNNSGLFFLCGNGVYKGFPPKWSGRCGLGYLVPSLTRYLTLNASQITNLRSFIHKVTPHR

cat input.txt | grep -v ">" | fold -w1 | sort | uniq -c



   6 A
   9 C
  10 D
   1 E
   7 F
  18 G
   5 H
   4 I
   7 K
  21 L
  15 N
   7 P
   6 Q
  11 R
  16 S
  18 T
   7 V
   8 W
   7 Y

однако,Я хочу рассчитать для всех лучше и эффективнее, особенно когда данные огромны

Ответы [ 4 ]

0 голосов
/ 18 февраля 2019

Попробуйте это решение Perl для повышения производительности.

$ perl -lne ' 
            if( ! /^>/ ) { while(/./g) { $kv{$&}++} }  
        END { for ( sort keys %kv) { print "$_ $kv{$_}" }} 
' learner.txt
A 107
C 41
D 102
E 132
F 65
G 140
H 52
I 84
K 114
L 174
M 39
N 67
P 107
Q 88
R 101
S 168
T 115
V 101
W 27
Y 30

$

Еще одно решение с использованием Perl, оптимизированное для производительности.

$ time perl -lne ' 
     if( ! /^>/ ) { for($i=0;$i<length($_);$i++) 
     { $x=substr($_,$i,1); $kv{$x}++ } }  
    END { for ( sort keys %kv) { print "$_ $kv{$_}" }} 
' chrY.fa
A 2994088
C 1876822
G 1889305
N 30812232
T 3002884
a 4892104
c 3408967
g 3397589
n 140
t 4953284

real    0m15.922s
user    0m15.750s
sys     0m0.108s

$

Редактирование с дальнейшей оптимизацией производительности

Все значения времени, указанные ниже, представляют собой в среднем более 3-5 запусков на рабочем столе, которые выполняются примерно в одно и то же время, но меняются местами во избежание явных эффектов кэширования.

Изменение стиля Cfor loop to for my $i (0..length($_)) ускоряет второе решение с 9,2 секунды до 6,8 секунды.

Затем, также удаляя скаляр ($x) при каждой операции, с

if (not /^>/) { for $i (0..length($_)) { ++$kv{ substr($_,$i,1) } } }

ускоряет это до 5,3 секунд .

Дальнейшее сокращение использования переменных путем копирования $_ и, таким образом, освобождения цикла для использования $_

if (not /^>/) { $l=$_; ++$kv{ substr($l,$_,1) } for 0..length($l) }

только немного помогает, работает на 5,2 секунды *

Конечно, ничто из этого не может сравниться с оптимизированной bioawk (C-кодом?) Программой.Для этого нам нужно написать это на C (что не очень сложно, используя Inline C).

Обратите внимание, что удаление дополнительного вызова (к substr) для каждого символа с помощью

if (not /^>/) { ++$kv{$_} for split //; }

приводит к «только» среднему 6.4 секундам , не так хорошо, как вышеуказанные настройки;это было сюрпризом.

Это время на рабочем столе с v5.16.На v5.24 на той же машине время наилучшего случая (substr без дополнительных переменных в цикле) составляет 4,8 секунды, в то время как время без substr (но с split) составляет 5,8 секунды.Приятно видеть, что более новые версии Perl работают лучше, по крайней мере, в этих случаях.

Для справки и упрощения синхронизации другими людьми, полный код для лучшего запуска

time perl -lne'
    if (not /^>/) { $l=$_; ++$kv{ substr($l,$_,1) } for 0..length($l) }
    END { for ( sort keys %kv) { print "$_ $kv{$_}" }}
' chrY.fa
0 голосов
/ 15 февраля 2019

С любым awk в любой оболочке на любой машине UNIX:

$ cat tst.awk
!/^>/ {
    for (i=1; i<=length($0); i++) {
        cnt[substr($0,i,1)]++
    }
}
END {
    for (char in cnt) {
        print char, cnt[char]
    }
}

$ awk -f tst.awk file
A 107
N 67
P 107
C 41
Q 88
D 102
E 132
R 101
F 65
S 168
G 140
T 115
H 52
I 84
V 101
W 27
K 114
Y 30
L 174
M 39

или, если вы предпочитаете:

$ awk -v ORS= '!/^>/{gsub(/./,"&\n"); print}' file | sort | uniq -c
    107 A
     41 C
    102 D
    132 E
     65 F
    140 G
     52 H
     84 I
    114 K
    174 L
     39 M
     67 N
    107 P
     88 Q
    101 R
    168 S
    115 T
    101 V
     27 W
     30 Y
0 голосов
/ 15 февраля 2019

Подсчет символов в строках можно легко выполнить с помощью awk.Для этого вы используете функцию gsub:

gsub(ere, repl[, in]) Ведите себя как sub (см. Ниже), за исключением того, что она должна заменить все вхождениярегулярное выражение (например, глобальный заменитель утилиты ed) в $0 или в аргументе in, если он указан.

sub(ere, repl[, in ]) Заменить строку repl вместопервый экземпляр расширенного регулярного выражения ERE в строке в и возвращаем количество подстановок . Если in опущен, awk должен использовать вместо него текущую запись ($0).

source: Awk Posix Standard

Следующие две функции выполняют подсчет следующим образом:

function countCharacters(str) {
    while(str != "") { c=substr(str,1,1); a[toupper[c]]+=gsub(c,"",str) }
}

или если может появиться много одинаковых последовательных символов, следующее решение может сбритьвыключено на пару секунд.

function countCharacters2(str) {
    n=length(str)
    while(str != "") { c=substr(str,1,1); gsub(c"+","",str);
       m=length(str); a[toupper[c]]+=n-m; n=m
    }
}

Ниже вы найдете 4 реализации, основанные на первой функции.Первые два запускаются на стандартном awk, последние два - на оптимизированной версии для fasta-файлов:

1.Читайте последовательность и обрабатывает ее построчно:

awk '!/^>/{s=$0; while(s!="") { c=substr(s,1,1); a[c]+=gsub(c,"",s) } }
     END {for(c in a) print c,a[c]}' file

2.объединить все последовательности и обработать их в конце:

awk '!/^>/{s=s $0 }
     END {while(s!="") { c=substr(s,1,1); a[c]+=gsub(c,"",s) }
         for(c in a) print c,a[c]}' file

3.То же, что 1, но используйте bioawk:

bioawk -c fastx '{while ($seq!=""){ c=substr($seq,1,1);a[c]+=gsub(c,"",$seq) } }
                 END{ for(c in a) print c,a[c] }' file

4.То же, что 2, но используйте bioawk:

bioawk -c fastx '{s=s $seq}
                 END { while(s!="") { c=substr(s,1,1); a[c]+=gsub(c,"",s) }
                       for(c in a) print c,a[c]}' file

Вот некоторые временные результаты, основанные на этом файле fasta

OP            : grep,sort,uniq : 47.548 s
EdMorton 1    : awk            : 39.992 s
EdMorton 2    : awk,sort,uniq  : 53.965 s
kvantour 1    : awk            : 18.661 s
kvantour 2    : awk            :  9.309 s
kvantour 3    : bioawk         :  1.838 s
kvantour 4    : bioawk         :  1.838 s
karafka       : awk            : 38.139 s
stack0114106 1: perl           : 22.754 s
stack0114106 2: perl           : 13.648 s
stack0114106 3: perl (zdim)    :  7.759 s

Примечание: BioAwk основан на awk Брайана Кернигана , который задокументирован в "Языке программирования AWK" Аль Ахо, Брайана Кернигана и ПитераВайнбергер (Addison-Wesley, 1988, ISBN 0-201-07981-X) .Я не уверен, что эта версия совместима с POSIX .

0 голосов
/ 15 февраля 2019

не уверен, насколько быстрее это будет, но если вы попробуете, пожалуйста, опубликуйте ваши сроки

$ awk '!/^>/ {n=split($0,a,""); for(i=1;i<=n;i++) c[a[i]]++} 
       END   {for(k in c) print k,c[k]}' file | sort

A 6
C 9
D 10
E 1
F 7
G 18
H 5
I 4
K 7
L 21
N 15
P 7
Q 6
R 11
S 16
T 18
V 7
W 8
Y 7

этот отчет учитывается для файла, а не построчно.Как отмечено ниже, не все awk поддерживают разделение пустой строки.

Ниже приведены временные параметры трех подходов:

$ time grep -v ">" filey | fold -w1 | sort | uniq -c >/dev/null

real    0m11.470s
user    0m11.746s
sys     0m0.260s

$ time awk '{n=split($0,a,""); for(i=1;i<=n;i++) c[a[i]++]} END{for(k in c) print k,c[k]}' filey >/dev/null 

real    0m7.441s
user    0m7.334s
sys     0m0.060s

$ time awk '{n=length($0); for(i=1;i<=n;i++) c[substr($0,i,1)]++} END{for(k in c) print k,c[k]}' filey >/dev/null

real    0m5.055s
user    0m4.979s
sys     0m0.047s

для тестового файла

$ wc filey

  118098   649539 16828965 filey

меня удивило, что substr быстрее, чем split.Возможно из-за выделения массива.

...