Perl: удаление дубликатов из большого набора данных - PullRequest
6 голосов
/ 19 апреля 2011

Я использую Perl для генерации списка уникальных экзонов (которые являются единицами генов).

Я создал файл в этом формате (с сотнями тысяч строк):

chr1 1000 2000 ген1

chr1 3000 4000 ген2

chr1 5000 6000 gene3

chr1 1000 2000 ген4

Позиция 1 - это хромосома, позиция 2 - начальная координата экзона, позиция 3 - конечная координата экзона, а позиция 4 - имя гена.

Поскольку гены часто строятся из разных расположений экзонов, у вас один и тот же экзон в нескольких генах (см. Первый и четвертый наборы). Я хочу удалить эти «дубликаты» - т.е. удалить gene1 или gene4 (не важно, какой из них удаляется).

Я часами бился головой о стену, пытаясь сделать то, что (я думаю) является простой задачей. Кто-нибудь может указать мне правильное направление (я)? Я знаю, что люди часто используют хеши для удаления дублирующих элементов, но это не совсем дубликаты (так как имена генов разные). Также важно, чтобы я не потерял имя гена. В противном случае это было бы проще.

Вот совершенно неработающий цикл, который я пробовал. В массиве «exons» каждая строка хранится в виде скаляра, отсюда и подпрограмма. Не смейся Я знаю, что это не работает, но, по крайней мере, вы можете видеть (я надеюсь), что я пытаюсь сделать:

for (my $i = 0; $i < scalar @exons; $i++) {
my @temp_line = line_splitter($exons[$i]);                      # runs subroutine turning scalar into array
for (my $j = 0; $j < scalar @exons_dup; $j++) {
    my @inner_temp_line = line_splitter($exons_dup[$j]);        # runs subroutine turning scalar into array
    unless (($temp_line[1] == $inner_temp_line[1]) &&           # this loop ensures that the the loop
            ($temp_line[3] eq $inner_temp_line[3])) {           # below skips the identical lines
                if (($temp_line[1] == $inner_temp_line[1]) &&   # if the coordinates are the same
                    ($temp_line[2] == $inner_temp_line[2])) {   # between the comparisons
                        splice(@exons, $i, 1);                  # delete the first one
                    }
            }
}
* *} Тысяча двадцать-один

Ответы [ 4 ]

7 голосов
/ 19 апреля 2011
my @exons = (
    'chr1 1000 2000 gene1',
    'chr1 3000 4000 gene2',
    'chr1 5000 6000 gene3',
    'chr1 1000 2000 gene4'
);

my %unique_exons = map { 
    my ($chro, $scoor, $ecoor, $gene) = (split(/\s+/, $_));
    "$chro $scoor $ecoor" => $gene
} @exons;

print "$_ $unique_exons{$_} \n" for keys %unique_exons;

Это даст вам уникальность, и будет включено последнее имя гена. В результате:

chr1 1000 2000 gene4 
chr1 5000 6000 gene3 
chr1 3000 4000 gene2
3 голосов
/ 19 апреля 2011

Вы можете использовать хэш для дедупликации en passant, но вам нужен способ объединения частей, которые вы хотите использовать для обнаружения дубликатов, в одну строку.

sub extract_dup_check_string {
    my $exon = shift;
    my @parts = line_splitter($exon);
    # modify to suit:
    my $dup_check_string = join( ';', @parts[0..2] );
    return $dup_check_string;
}

my %seen;
@deduped_exons = grep !$seen{ extract_dup_check_string($_) }++, @exons;
1 голос
/ 19 апреля 2011

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

#!/usr/bin/env perl                                                                                                                                                                                                                   

use strict;
use warnings;

my %seen;
while (my $line = <>) {

  my($chromosome, $exon_start, $exon_end, $gene) = split /\s+/, $line;
  my $key = join ':', $chromosome, $exon_start, $exon_end;

  if ($seen{$key}) {
    next;
  }
  else {
    $seen{$key}++;
    print $line;
  }

}
0 голосов
/ 19 апреля 2011

Так же просто, как и приходит.Я старался использовать как можно меньше магии.

my %exoms = ();
my $input;

open( $input, '<', "lines.in" ) or die $!;

while( <$input> )
{
    if( $_ =~ /^(\w+\s+){3}(\w+)$/ ) #ignore lines that are not in expected format
    {
        my @splits = split( /\s+/, $_ ); #split line in $_ on multiple spaces

        my $key = $splits[1] . '_' . $splits[2];

        if( !exists( $exoms{$key} ) )
        {
            #could output or write to a new file here, probably output to a file
            #for large sets.
            $exoms{$key} = \@splits; 
        }
    }

}


#demo to show what was parsed from demo input
while( my ($key, $value) = each(%exoms) )
{
    my @splits = @{$value};
    foreach my $position (@splits)
    {
        print( "$position " );
    }
    print( "\n" );

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...