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%.