1

我想计算核苷酸的出现次数(字符串中的字母“A、T、G、C”)。我试图为此使用tr///运算符,但它在下面的代码中每次都返回零计数。

仅当我在tr///运算符中使用变量时才会发生这种情况。如果我单独输入单个字母,它就可以工作。我想知道我们是否可以在tr///运算符中使用变量进行模式匹配(和计数)。如果可以的话,有人告诉我如何修改我的代码。

稍后我计划计算密码子的数量(~64)。因此麻烦。珍惜你的时间。谢谢!

#!/usr/bin/perl

use strict;
use warnings;

my $orf = "ATGCTAGCTAGCATAGAGCTAGCTA";
my @atgc = qw(A T G C);
my %hash = ();

foreach my $nt(@atgc) {
  $hash{$nt} = ($orf =~ tr/$nt//);
}
4

6 回答 6

4

“”中没有“ $”、“ n”或“ t”的实例ATGCTAGCTAGCATAGAGCTAGCTA,因此tr正确返回零。

如果要构建tr///运算符,则需要对其进行解析和编译。

my %counts;
for my $nt (@atgc) {
   $counts{$nt} = eval "\$orf =~ tr/\Q$nt\E//";
}

但我不会使用tr///.

my %atgc = map { $_ => 1 } @atgc;
my %counts;
++$counts{$nt} for grep $atgc{$_}, split //, $orf;
于 2012-05-01T16:37:38.490 回答
4

不幸的是,Perl 不会将变量插入到tr///. 您将不得不使用正则表达式:

use strict;
use warnings;

my $orf = "ATGCTAGCTAGCATAGAGCTAGCTA";
my @atgc = qw(A T G C);
my %count;

$count{$1}++ while $orf =~ /([@atgc])/g;

printf "%s => %d\n", $_, $count{$_} for @atgc;

输出

A => 8
T => 6
G => 6
C => 5

谨防

这不是 - 正如它可能出现的那样 - 匹配字符串数组中的任何一个的通用解决方案。正在发生的事情是@atcg被插入到正则表达式中,就好像它在一个双引号字符串中一样。这意味着 Perl 将使用$"内置变量(默认设置为单个空格)将其转换为等效于join $", @atgc.

所以代码实际上看起来像

$count{$1}++ while $orf =~ /([A T G C])/g;

它将匹配空格和字母,并且如果@atgc在正则表达式字符类中包含任何特殊内容,例如^,]-.

不必要地计算空格应该不是问题,但是如果您的列表可能包含符号,那么这不是您应该使用的解决方案。

每个 ASCII 字符的计数可以安全地写为

$count{$1}++ while $orf =~ /(.)/sg;

%count并且可以简单地忽略散列中不需要的信息。

于 2012-05-01T16:37:39.193 回答
3
$hash{$nt} = eval "\$orf =~ tr/\Q$nt\E//"

应该做的工作。也许这不是最有效的解决方案。

于 2012-05-01T16:35:32.063 回答
1

你可以使用s///g

#!/usr/bin/env perl

use strict; use warnings;

my $orf = "ATGCTAGCTAGCATAGAGCTAGCTA";
my @atgc = qw(A T G C);

my %hash = map {$_ => $orf =~ s/$_/$_/g } @atgc;

输出:

---
答:8
C: 5
克:6
时间:6

如果源字符串真的很长,您可以将其视为保存一段时间的内容并一次读取一个字节,假设字符保证为 ASCII。

#!/usr/bin/env perl

use strict; use warnings;

my $orf = "ATGCTAGCTAGCATAGAGCTAGCTA";
my %atgc = map { $_ => undef } qw(A T G C);

use YAML;
print Dump count(\$orf, \%atgc);

sub count {
    my $src_ref = shift;
    my $lookup = shift;

    my %count;

    open my $src, '<', $src_ref or die $!;
    {
        local $/ = \1;
        exists $lookup->{$_} and $count{ $_ } += 1 while <$src>;
    }
    close $src;

    return \%count;
}
于 2012-05-01T17:40:49.343 回答
1

您不需要正则表达式等来执行此操作,您只需要遍历一个字符串:

my $orf = "ATGCTAGCTAGCATAGAGCTAGCTA";
my %nt;

$nt{$_}++ foreach (split('', $orf));
于 2012-05-01T18:31:31.633 回答
1

做一个模式编译器。

sub make_counter {
  my @sequences = @_;

  my $pattern = "(?:" . join("|", map quotemeta, @sequences) . ")";

  my $compiled = eval q<
    sub {
      local($_) = @_;
      my $n = () = /$pattern/g;
    }
  >;

  if (defined $compiled) {
    return $compiled;
  }
  else {
    die "$0: internal: counter compilation failed:\n$@\n";
  }
}

使用quotemeta,我们强制序列中的所有字符仅匹配自身,没有特殊含义。Perl FAQ 的第 4 节描述了计数匹配的时髦位:

另一个版本在列表上下文中使用全局匹配,然后将结果分配给一个标量,产生匹配数的计数。

$count = () = $string =~ /-\d+/g;

请注意,它可以容忍序列中的垃圾。例如,用

my @nucleotides = qw/ G A T C /;

my $numnuc = make_counter @nucleotides;
print $numnuc->("xGxAxTxxxxTyA1C2A"), "\n";

输出:

7

计数密码子

my @codons = qw(
  TTT TCT TAT TGT TTC TCC TAC TGC TTA TCA TAA TGA
  TTG TCG TAG TGG CTT CCT CAT CGT CTC CCC CAC CGC
  CTA CCA CAA CGA CTG CCG CAG CGG ATT ACT AAT AGT
  ATC ACC AAC AGC ATA ACA AAA AGA ATG ACG AAG AGG
  GTT GCT GAT GGT GTC GCC GAC GGC GTA GCA GAA GGA
  GTG GCG GAG GGG
);

my $numcod = make_counter @codons;
print $numcod->("GAG-GGG!AGG,TAT#TTT");

请注意,任何垃圾(如果存在)必须出现密码子序列之间。

输出:

5
于 2012-05-01T19:37:02.980 回答