计算字符串第n个字符周围的重复字符

时间:2014-10-26 11:59:50

标签: regex perl

对于我大学项目的一部分,我试图计算21 bp DNA序列的第11个字符周围的碱基重复序列。我想查看第11个字符,然后如果周围有重复的相同字符,则打印它们。

例如:

GCTAAAGTAAAAGAAGATGCA

会给出结果:

11th base is A, YES repeated 4 times

我真的不知道该怎么做,为了获得第11个角色,我确信我可以使用正则表达式,但之后我不确定。

首先,我使用哈希来寻找每个序列中不同核苷酸基团的出现次数,如下所示:

#!/usr/bin/perl
use strict;
use warnings;
use autodie;

my $outputfile = "/Users/edwardtickle/Documents/hash.txt";

open FILE1, "/Users/edwardtickle/Documents/randomoutput.txt";

open( OUTPUTFILE, ">$outputfile" );

while (<FILE1>) {
    if (/^(\S+)/) {

        my $dna     = $1;
        my @repeats = ( $dna =~ /[A]{3}/g );
        my @changes = ( $dna =~ /[T]{2}/g );

        my %hash  = ();
        my %hash1 = ();

        for my $repeats (@repeats) {
            $hash{$repeats}++;
        }
        for my $changes (@changes) {
            $hash1{$changes}++;
        }

        for my $key ( keys %hash ) {
            print OUTPUTFILE $key . " =>  " . $hash{$key} . "\n";
        }
        for my $key1 ( keys %hash1 ) {
            print OUTPUTFILE $key1 . " =>  " . $hash1{$key1} . "\n";
        }
    }
}

FILE1数据:

ATTTTTAGCCGAACAAGTACC
TACTTAGTTAAATTGTTACAA
ATAAACCTTGTGCAGGTTTGT
CCTTAATCCTTGTATTTTTAA
TCTTGTTAAAATGTCTACAGG
ATGTTAGTTATTTCATTCTTC
AAGTAACTAAAATTGCTCAAT
ACATTCGACAAAAATGAAAAA
TGTTTCGAATTCACCATATGC
AGTCGCAGCGGGTGCTCCAGC

给出结果:

TT =>  2
AAA =>  1
TT =>  4
AAA =>  1
TT =>  2
TT =>  4
AAA =>  1
TT =>  2
TT =>  5
AAA =>  1
TT =>  1
AAA =>  2
TT =>  1
TT =>  2

对于此样本数据集,我想要每个序列的累积计数,而不是每个匹配字符串中的单个出现次数,如下所示:

AAA =>  6
TT => 23

我如何改变输出?我如何防止一串TTTTT碱基显示为TT =&gt; 2?然后,如果有人有任何关于如何解决原始问题的建议/如果有可能,那将非常感激。

提前致谢!

2 个答案:

答案 0 :(得分:1)

此代码应该满足您的需求。实际上没有正则表达式可以在给定字符位置和周围找到给定字符的最长序列。此代码的工作原理是将字符串$seq拆分为一个字符数组@seq,然后从中心向前和向后搜索。

以这种方式做事是切实可行的,因为序列相对较短,只要字符串中有奇数个字符,它就会为你计算中心点。

use strict;
use warnings;

while (<DATA>) {
  chomp;
  my ($base, $length) = find_mid_band($_);
  printf "%s => %d\n", $base, $length;
}


sub find_mid_band {
  my ($seq) = @_;
  my @seq = unpack '(A1)*', $seq;

  my $len_seq = @seq;
  my $c_offset = ($len_seq - 1) / 2;
  my $c_char = $seq[$c_offset];

  my ($start, $end) = ($c_offset, $c_offset + 1);
  --$start while $start > 0        and $seq[$start-1] eq $c_char;
  ++$end   while $end   < $len_seq and $seq[$end]     eq $c_char;

  return $c_char, $end-$start;
}

__DATA__
ATTTTTAGCCGAACAAGTACC
TACTTAGTTAAATTGTTACAA
ATAAACCTTGTGCAGGTTTGT
CCTTAATCCTTGTATTTTTAA
TCTTGTTAAAATGTCTACAGG
ATGTTAGTTATTTCATTCTTC
AAGTAACTAAAATTGCTCAAT
ACATTCGACAAAAATGAAAAA
TGTTTCGAATTCACCATATGC
AGTCGCAGCGGGTGCTCCAGC

<强>输出

G => 1
A => 3
T => 1
T => 2
A => 4
T => 3
A => 4
A => 5
T => 2
G => 3

<强>更新

这是一个更好的方法。它越来越短,并且在相同字符的所有子序列中工作,直到找到跨越中间字符的序列。

输出与上述输出相同。

use strict;
use warnings;

while (<DATA>) {
  chomp;
  my ($base, $length) = find_mid_band($_);
  printf "%s => %d\n", $base, $length;
}


sub find_mid_band {
  my ($seq) = @_;
  my $mid_seq = length($seq) / 2;
  while ( $seq =~ /(.)\1*/g ) {
    if ($-[0] < $mid_seq and $+[0] > $mid_seq) {
      return $1, $+[0]-$-[0];
    }
  }
}

__DATA__
ATTTTTAGCCGAACAAGTACC
TACTTAGTTAAATTGTTACAA
ATAAACCTTGTGCAGGTTTGT
CCTTAATCCTTGTATTTTTAA
TCTTGTTAAAATGTCTACAGG
ATGTTAGTTATTTCATTCTTC
AAGTAACTAAAATTGCTCAAT
ACATTCGACAAAAATGAAAAA
TGTTTCGAATTCACCATATGC
AGTCGCAGCGGGTGCTCCAGC

答案 1 :(得分:1)

使用正则表达式:

use strict;
use warnings;

my $char = 11;  # Looking for the 11th character, or position 10.

while (<DATA>) {
    chomp;
    if (m{
        ( (.) \2*+ )        #  Look for a repeated character sequence
        (?<= .{$char} )     #  Must include pos $char - 1
    }x) {
        printf "%s => %d\n", $2, length($1);
    }
}

__DATA__
ATTTTTAGCCGAACAAGTACC
TACTTAGTTAAATTGTTACAA
ATAAACCTTGTGCAGGTTTGT
CCTTAATCCTTGTATTTTTAA
TCTTGTTAAAATGTCTACAGG
ATGTTAGTTATTTCATTCTTC
AAGTAACTAAAATTGCTCAAT
ACATTCGACAAAAATGAAAAA
TGTTTCGAATTCACCATATGC
AGTCGCAGCGGGTGCTCCAGC

输出:

G => 1
A => 3
T => 1
T => 2
A => 4
T => 3
A => 4
A => 5
T => 2
G => 3