Perl найти количество совпадающих 2 символов в строке
Есть ли в perl метод (не bioperl), чтобы найти количество каждых 2 последовательных букв
то есть: количество AA, AC,AG,AT,CC,CA...
в такой последовательности:
$sequence = 'AACGTACTGACGTACTGGTTGGTACGA'
PS: мы можем сделать это вручную, используя регулярное выражение, т.е. $GC=($sequence=~s/GC/GC/g), которое возвращает номер GC в последовательности. Мне нужен автоматический и общий способ, спасибо в Advane
3 ответа
Вы меня запутали некоторое время, но я понимаю, что вы хотите, чтобы посчитать динуклеотиды в данной строке.
Код:
my @dinucs = qw(AA AC AG CC CA CG);
my %count;
my $sequence = 'AACGTACTGACGTACTGGTTGGTACGA';
for my $dinuc (@dinucs) {
$count{$dinuc} = ($sequence =~ s/\Q$dinuc\E/$dinuc/g);
}
Вывод из Data:: Dumper:
$VAR1 = {
"AC" => 5,
"CC" => "",
"AG" => "",
"AA" => 1,
"CG" => 3,
"CA" => ""
};
Близко к ответу TLP, но без замены:
my $sequence = 'AACGTACTGACGTACTGGTTGGTACGA';
my @dinucs = qw(AA AC AG AT CC CG);
my %count = map{$_ => 0}@dinucs;
for my $dinuc (@dinucs) {
while($sequence=~/$dinuc/g) {
$count{$dinuc}++;
}
}
Ориентир:
my $sequence = 'AACGTACTGACGTACTGGTTGGTACGA';
my @dinucs = qw(AA AC AG AT CC CG);
my %count = map{$_ => 0}@dinucs;
my $count = -3;
my $r = cmpthese($count, {
'match' => sub {
for my $dinuc (@dinucs) {
while($sequence=~/$dinuc/g) {
$count{$dinuc}++;
}
}
},
'substitute' => sub {
for my $dinuc (@dinucs) {
$count{$dinuc} = ($sequence =~ s/\Q$dinuc\E/$dinuc/g);
}
}
});
выход:
Rate substitute match
substitute 13897/s -- -11%
match 15622/s 12% --
Regex работает, если вы осторожны, но есть простое решение с использованием substr, которое будет быстрее и более гибким.
(Начиная с этой публикации, решение для регулярных выражений, помеченное как принятое, не сможет правильно подсчитать динуклеотиды в повторяющихся областях, таких как "AAAA...", которых много в естественных последовательностях. Как только вы сопоставите "AA", поиск по регулярному выражению возобновится на третьем символе, пропуская средний динуклеотид "AA". Это не влияет на другие динуки, поскольку, если у вас есть "AC" в одной позиции, вы гарантированно не будете иметь его на следующей базе, естественно. приведенный в вопросе не пострадает от этой проблемы, так как ни одна база не появляется три раза подряд.)
Метод, который я предлагаю, является более гибким в том смысле, что он может считать слова любой длины; Расширить метод регулярных выражений до более длинных слов сложно, так как для получения точного подсчета вам придется выполнять еще больше упражнений с вашим регулярным выражением.
sub substrWise {
my ($seq, $wordLength) = @_;
my $cnt = {};
my $w;
for my $i (0 .. length($seq) - $wordLength) {
$w = substr($seq, $i, $wordLength);
$cnt->{$w}++;
}
return $cnt;
}
sub regexWise {
my ($seq, $dinucs) = @_;
my $cnt = {};
for my $d (@$dinucs) {
if (substr($d, 0,1) eq substr($d, 1,1) ) {
my $n = substr($d, 0,1);
$cnt->{$d} = ($seq =~ s/$n(?=$n)/$n/g); # use look-ahead
} else {
$cnt->{$d} = ($seq =~ s/$d/$d/g);
}
}
return $cnt;
}
my @dinucs = qw(AA AC AG AT CA CC CG CT GA GC GG GT TA TC TG TT);
my $sequence = 'AACGTACTGACGTACTGGTTGGTACGA';
use Test::More tests => 1;
my $rWise = regexWise($sequence, \@dinucs);
my $sWise = substrWise($sequence, 2);
$sWise->{$_} //= '' for @dinucs; # substrWise will not create keys for words not found
# this seems like desirable behavior IMO,
# but i'm adding '' to show that the counts match
is_deeply($rWise, $sWise, 'verify equivalence');
use Benchmark qw(:all);
cmpthese(100000, {
'regex' => sub {
regexWise($sequence, \@dinucs);
},
'substr' => sub {
substrWise($sequence, 2);
}
Выход:
1..1
ok 1 - verify equivalence
Rate regex substr
regex 11834/s -- -85%
substr 76923/s 550% --
Для более длинных последовательностей (10-100kbase) преимущество не столь выражено, но оно все же выигрывает примерно на 70%.