Генерация случайного подмножества последовательностей из файла fasta

Привет мастерам Perl в мире.

У меня еще одна проблема с программированием. Я кодирую программу, которая выбирает случайные последовательности из файла proteom fasta с определенным входным номером.

Общий фаст-файл выглядит так:

> seq_ID_1 описания и т. д. ASDGDSAHSAHASDFRHGSDHSDGEWTSHSDHDSHFSDGSGASGADGHHAH ASDSADGDASHDASHSAREWAWGDASHASGASGASGSDGASDGDSAHSHAS SFASGDASGDSSDFDSFSDFSD

> seq_ID_2 описания и т. д. ASDGDSAHSAHASDFRHGSDHSDGEWTSHSDHDSHFSDGSGASGADGHHAH ASDSADGDASHDASHSAREWAWGDASHASGASGASG

и так далее.......

Буквы обозначают пептиды аминокислот.

Итак, у меня есть файл fasta с 1000 последовательностями, и я хочу получить 63,21% из них, что будет 632,1 последовательности. Но последовательность не может быть числом с плавающей запятой, поэтому, если оно превышает 0,5, я хочу округлить, а если меньше 0,5, округлить вниз.

Это мой код для генерации подмножества случайной последовательности, но он немного не так хорош в работе.

#!/usr/bin/perl

#Selecting 63.21% of random sequnces from a proteom file.
use strict;
use warnings;
use List::Util qw(shuffle);

#Give the first argument as a proteom file.
if (@ARGV != 1)
{
    print "Invalid arguments\n";
    print "Usage: perl randseq.pl [proteom_file]";
    exit(0);
}

my $FILE = $ARGV[0];
my $i = 0;
my %protseq = {};
my $nIdx = 0;

#Extraction and counting of the all headers from a proteom file.
open(LIST,$FILE);
open(TEMP1, ">temp1");
while (my $line = <LIST>){
    chomp $line;
    if ($line =~ />(\S+) (.+)/){
        $i++;
        print TEMP1 $1,"\n";
    }
}
close(LIST);
close(TEMP1);

#Selection of random headers for generating a random subset of the proteom file.
my $GET_LINES = RoundToInt ($i*0.6321);

my @line_starts;
open(my $FH,'<','temp1');
open(TEMP2, ">temp2");

do {
     push @line_starts, tell $FH
} while ( <$FH> );

my $count = @line_starts;

my @shuffled_starts = (shuffle @line_starts)[1..$GET_LINES+1];

for my $start ( @shuffled_starts ) {

     seek $FH, $start, 0
         or die "Unable to seek to line - $!\n";

     print TEMP2 scalar <$FH>;
}
close(TEMP2);

#Assigning the sequence data to randomly generated header file.
open(DATA,'<','temp2');
while(my $line = <DATA>)
{
    chomp($line);
    $line =~ s/[\t\s]//g;
    if($line =~ /^([^\s]+)/)
    {
        $protseq{$1}++;
    }
}
close(DATA);

open(DATA, "$FILE");
open(OUT, ">random_seqs.fasta");
while(my $line = <DATA>)
{
    chomp($line);
    if($line =~ /^>([^\s]+)/)
    {
        if($protseq{$1} ne "")
        {

            $nIdx = 1;
            print OUT "$line\n";
        }
        else
        {
            $nIdx = 0;
        }
    }
    else
    {
        if($nIdx == 1)
        {
            print OUT "$line\n";
        }
    }
}
close(DATA);
close(OUT);

#subroutine for rounding
sub RoundToInt {
  int($_[0] + .5 * ($_[0] <=> 0));
}

system("erase temp1");
system("erase temp2");
exit;

Однако иногда он дает правильное количество последовательностей, а иногда и еще одну последовательность. Как я могу избавиться от этого ... какие-нибудь идеи, пожалуйста?

или может лучше код покороче?

здесь вы можете получить 75 протеомных файлов дрожжей. [http://www.peroxisomedb.org/Download/Saccharomyces_cerevisiae.fas visible[1]

Надеюсь, я скоро это исправлю ... :(


person Karyo    schedule 21.03.2013    source источник
comment
Я подозреваю, что использование вами оператора космического корабля в вашей процедуре округления может быть проблемой, как и везде, кажется, нормально. Я могу попробовать int($float + 0.5), как рекомендовано в этом вопросе.   -  person learner    schedule 21.03.2013
comment
Спасибо за комментарий, ученик. Я изменил [my $ GET_LINES = RoundToInt ($ i * 0.6321);] на [my $ GET_LINES = int ($ i * 0.6321 + 0.5);], но проблема осталась прежней. Мне жаль.   -  person Karyo    schedule 21.03.2013
comment
Кроме того, почему у вас [1..$GET_LINES+1];? Если я что-то не понимаю, $i - это int = length (FASTA seqs), так почему вы планируете еще одну строку?   -  person learner    schedule 21.03.2013
comment
$ i - это общее количество последовательностей из входного файла протеома, и я хочу выбрать 63,21% последовательностей. Я пытался манипулировать, чтобы получить правильное число, поэтому я складывал и вычитал, чтобы получить правильное число. +1 предположим, что это -1, потому что оно начинается с 0. Не так ли?   -  person Karyo    schedule 21.03.2013
comment
Вы должны проверить, является ли $GET_LINES сам по себе правильным номером для данного файла, просто добавив вызов print(). Я думаю, это будет правильно - вы начинаете с 0, и когда вы встречаетесь с первой строкой, у вас есть 1, 2 во второй и т. Д., Просто глядя на свой цикл for.   -  person learner    schedule 21.03.2013
comment
какое это имеет отношение к регулярному выражению ??   -  person CSᵠ    schedule 21.03.2013
comment
@ kaᵠ видите строку if ($line =~ />(\S+) (.+)/)?   -  person iain    schedule 21.03.2013


Ответы (2)


Ваш подход выглядит прекрасно, просто излишне сложным. Я бы сделал это так:

use strict;
use warnings;

# usage: randseq.pl [fraction] < input.fasta > output.fasta
my $fraction = (@ARGV ? shift : 0.6321);

# Collect input lines into an array of sequences:
my @sequences;
while (<>) {
    # A leading > starts a new sequence. (The "\" is only there to
    # avoid confusing the Stack Overflow syntax highlighting.)
    push @sequences, [] if /^\>/;
    push @{ $sequences[-1] }, $_;
}

# Calculate how many sequences we want:
my $n = @sequences;
my $k = int( $n * $fraction + 0.5 );
warn "Selecting $k out of $n sequences (", 100 * $k / $n, "%).\n";

# Do a partial Fisher-Yates shuffle to select $k random sequences out of $n:
foreach my $i (0 .. $k-1) {
    my $j = $i + int rand($n-$i);
    @sequences[$i,$j] = @sequences[$j,$i];
}

# Print the output:
print @$_ for @sequences[0 .. $k-1];

Обратите внимание, что этот код считывает все содержимое входного файла в память. Если входной файл слишком велик для этого, и вам нужна только небольшая его часть, можно использовать резервуар sampling, чтобы выбрать k случайных последовательностей из произвольно большой их коллекции, не удерживая больше:

use strict;
use warnings;

my $k = (@ARGV ? shift : 632);  # sample size: need to know this in advance

# Use reservoir sampling to select $k random sequences:
my @samples;
my $n = 0;  # total number of sequences read
my $i;      # index of current sequence
while (<>) {
    if (/^\>/) {
        # Select a random sequence from 0 to $n-1 to replace:
        $i = int rand ++$n;
        # Save all samples until we've accumulated $k of them:
        $samples[$n-1] = $samples[$i] if $n <= $k;
        # Only actually store the new sequence if it's one of the $k first ones:
        $samples[$i] = [] if $i < $k;
    }
    push @{ $samples[$i] }, $_ if $i < $k;
}

warn "Only read $n < $k sequences, selected all.\n" if $n < $k;
warn "Selected $k out of $n sequences (", 100 * $k / $n, "%).\n" if $n >= $k;

# Print sampled sequences:
print @$_ for @samples;

Однако, если вам действительно нужна определенная часть входных последовательностей, вам нужно сначала подсчитать их в отдельном проходе по файлу.

Обе программы, указанные выше, также равномерно перемешивают выбранные последовательности в качестве побочного эффекта. (Фактически, я намеренно изменил алгоритм выборки резервуара, чтобы сделать перемешивание единообразным для всех значений n и k.) Если вы этого не хотите, вы всегда можете перед печатью отсортируйте последовательности по любому критерию, который вы предпочитаете.

person Ilmari Karonen    schedule 21.03.2013
comment
+1 за излишне сложный комментарий. Сурово, но справедливо :) - person iain; 21.03.2013
comment
Я хочу поставить +99, но ставлю +1 из-за излишне сложного комментария. Спасибо за лучшую логику и код. - person Karyo; 22.03.2013

я использовал функцию spritf для круглых чисел и массивов вместо временных файлов

#!/usr/bin/perl

use strict;

if (@ARGV != 1)
{
    print "Invalid arguments\n";
    print "Usage: perl randseq.pl [proteom_file]";
    exit(0);
}

my $FILE = $ARGV[0];

open(LIST,"<$FILE");

my @peptides;
my $element;
while (my $line = <LIST>){
      if ($line =~ />.*/) {
      push (@peptides, $element);
      $element=$line;
      }
      else {
      $element.=$line;
      }
}
close(LIST);

my $GET_LINES = sprintf("%.0f",$#peptides*0.6321);

my @out;
for (0..$GET_LINES) {
    my $index=$#peptides;
    push (@out, $peptides[int(rand($index))]);
    splice(@peptides, $index, 1);
}

open (OUT, '>out.fasta');
foreach (@out) {
  print OUT $_."\n";
}

exit;
person Suic    schedule 21.03.2013
comment
Выглядит нормально, хотя splice может оказаться неэффективным для очень больших массивов. Хотя на практике это, вероятно, не имеет значения. Кроме того, />.*/ точно такой же, как просто />/, и также будет соответствовать строкам с > в середине, поскольку он не привязан. Чтобы сопоставить только строки с начальными символами >, вам потребуется /^>/. - person Ilmari Karonen; 21.03.2013