如果你小心,Regex 可以工作,但有一个使用 substr 的简单解决方案,它会更快、更灵活。
(截至本文发布时,the regex solution marked as accepted 将无法正确计算重复区域中的二核苷酸,例如 'AAAA...',其中许多是自然发生的序列。
匹配“AA”后,正则表达式搜索将在第三个字符处继续,跳过中间的“AA”二核苷酸。这不会影响其他二核苷酸,因为如果您在一个位置有“AC”,那么您自然可以保证它不会出现在下一个碱基中。问题中给出的特定序列不会出现这个问题,因为没有碱基连续出现 3 次。)
我建议的方法更灵活,它可以计算任意长度的单词;将正则表达式方法扩展到更长的单词很复杂,因为您必须使用正则表达式做更多的体操才能获得准确的计数。
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-100 kbase),优势不那么明显,但仍能胜出约 70%。