【问题标题】:extract overlapping regions提取重叠区域
【发布时间】:2014-04-10 03:05:08
【问题描述】:

我有一个描述基因组区域的文件,如下所示:

chrom   chromStart  chromEnd    PGB
chr1    12874   28371   2
chr1    15765   21765   1
chr1    15795   28371   2
chr1    18759   24759   1
chr1    28370   34961   1
chr3    233278  240325  1
chr3    239279  440831  2
chr3    356365  362365  1

基本上,PGB 描述了以染色体数 (chrom)、起始 (chromStart) 和结束 (chromEnd) 坐标为特征的基因组区域的类别。

我希望折叠重叠区域,以便 PGB = 1 和 2 的重叠区域属于新类别,PGB = 3。输出为:

chrom   chromStart  chromEnd    PGB
chr1    12874   15764   2
chr1    15765   24759   3
chr1    24760   28369   2
chr1    28370   28371   3
chr1    28372   34961   1
chr3    233278  239278  1
chr3    239279  240325  3
chr3    240326  356364  2
chr3    356365  440831  3

基本上我希望获得一个报告独特区域的输出文件。有两个标准。

首先,如果行之间的 PG​​B(第 4 列)相同,则合并范围。例如。

chrom   chromStart  chromEnd    PGB
chr1    1   10  1
chr1    5   15  1

输出

chrom   chromStart  chromEnd    PGB
chr1    1   15  1

其次,如果行之间的 PG​​B 不同,chr(第 1 列)相同,并且范围重叠(col2 和 3),则将重叠范围报告为 PGB = 3 以及它们各个类别唯一的范围。

例如。

chrom   chromStart  chromEnd    PGB
chr1    30  100 1
chr1    50  150 2

输出

chrom   chromStart  chromEnd    PGB
chr1    30  49  1
chr1    50  100 3
chr1    101 150 2

我希望这能更好地说明问题。

【问题讨论】:

  • 到目前为止你有没有尝试过?
  • 我对 perl/unix 很陌生,所以我在 excel 上手动操作。不幸的是,我有 60000 多行,所以我希望有一个更快的替代方案。
  • @user3222627 你需要多解释一下你是如何得到你想要的结果的。
  • @jaypal,看起来有范围,从 chromStart 到 chromEnd。每个范围都有一个数字 1 或 2 分配给它。如果一个范围为 1 的范围与一个范围为 2 的范围重叠,则重叠部分被指定为 PGB = 3。如果您足够接近地查看数据,您会看到它。这是一个棘手的转变;我不确定实现它的有效方法是什么。
  • Dave Cross 写了一本关于解决这类问题的优秀书籍。您可以将其作为 pdf 格式获取 man.lupaworld.com/content/develop/Perl/…

标签: perl awk bioinformatics bioperl


【解决方案1】:

我创建了一个脚本,我相信它可以实现这一目标。

use List::Util qw(max);

use strict;
use warnings;

# Skip Header row
<DATA>;

# With only 60k rows, going to just slurp all the data
my %chroms;
while (<DATA>) {
    chomp;
    my ($chrom, $start, $end, $pgb) = split ' ';

    # Basic Data Validation.
    warn "chromStart is NaN, '$start'" if $start =~ /\D/;
    warn "chromEnd is NaN, '$end'" if $end =~ /\D/;
    warn "range not ordered, '$start' to '$end'" if $start > $end;
    warn "PGB only can be 1 or 2, '$pgb'" if ($pgb ne '1' && $pgb ne '2');

    push @{$chroms{$chrom}{$pgb}}, [$start, $end];
}

print "chrom    chromStart  chromEnd    PGB\n";

# Process each Chrom
for my $chrom (sort keys %chroms) {
    for my $pgb (sort keys %{$chroms{$chrom}}) {
        my $ranges = $chroms{$chrom}{$pgb};

        # Sort Ranges
        @$ranges = sort {$a->[0] <=> $b->[0] || $a->[1] <=> $b->[1]} @$ranges;

        # Combine overlapping and continguous ranges.
        # - Note because we're dealing with integer ranges, 1-4 & 5-8 are contiguous
        for my $i (0..$#$ranges-1) {
            if ($ranges->[$i][1] >= $ranges->[$i+1][0] - 1) {
                $ranges->[$i+1][0] = $ranges->[$i][0];
                $ranges->[$i+1][1] = max($ranges->[$i][1], $ranges->[$i+1][1]);
                $ranges->[$i] = undef;
            }
        }
        @$ranges = grep {$_} @$ranges;
    }

    # Create pgb=3 for overlaps.
    # - Save old ranges into aliases, and then start fresh
    my $pgb1array = $chroms{$chrom}{1};
    my $pgb2array = $chroms{$chrom}{2};
    my @ranges;

    # Always working on the first range in each array, until one of the arrays is empty
    while (@$pgb1array && @$pgb2array) {
        # Aliases to first element
        my $pgb1 = $pgb1array->[0];
        my $pgb2 = $pgb2array->[0];

        # PGB1 < PGB2
        if ($pgb1->[1] < $pgb2->[0]) {
            push @ranges, [@{shift @$pgb1array}, 1]

        # PGB2 < PGB1
        } elsif ($pgb2->[1] < $pgb1->[0]) {
            push @ranges, [@{shift @$pgb2array}, 2]

        # There's overlap for all rest 
        } else {
            # PGB1start < PGB2start
            if ($pgb1->[0] < $pgb2->[0]) {
                push @ranges, [$pgb1->[0], $pgb2->[0]-1, 1];
                $pgb1->[0] = $pgb2->[0];

            # PGB2start < PGB1start
            } elsif ($pgb2->[0] < $pgb1->[0]) {
                push @ranges, [$pgb2->[0], $pgb1->[0]-1, 2];
                $pgb2->[0] = $pgb1->[0];
            }
            # (Starts are equal now)

            # PGB1end < PGB2end
            if ($pgb1->[1] < $pgb2->[1]) {
                $pgb2->[0] = $pgb1->[1] + 1;
                push @ranges, [@{shift @$pgb1array}, 3];

            # PGB2end < PGB1end
            } elsif ($pgb2->[1] < $pgb1->[1]) {
                $pgb1->[0] = $pgb2->[1] + 1;
                push @ranges, [@{shift @$pgb2array}, 3];

            # PGB2end = PGB1end
            } else {
                push @ranges, [@$pgb1, 3];
                shift @$pgb1array;
                shift @$pgb2array;
            }
        }
    }

    # Append whichever is left over
    push @ranges, map {$_->[2] = 1; $_} @$pgb1array;
    push @ranges, map {$_->[2] = 2; $_} @$pgb2array;

    printf "%-8s %-11s %-11s %s\n", $chrom, @$_ for (@ranges);
}

1;

__DATA__
chrom   chromStart  chromEnd    PGB
chr1    12871   12873   2
chr1    12874   28371   2
chr1    15765   21765   1
chr1    15795   28371   2
chr1    18759   24759   1
chr1    28370   34961   1
chr3    233278  240325  1
chr3    239279  440831  2
chr3    356365  362365  1

结果:

chrom    chromStart  chromEnd    PGB
chr1     12871       15764       2
chr1     15765       24759       3
chr1     24760       28369       2
chr1     28370       28371       3
chr1     28372       34961       1
chr3     233278      239278      1
chr3     239279      240325      3
chr3     240326      356364      2
chr3     356365      362365      3
chr3     362366      440831      2

注意:我创建这个主要是为了自己的练习,但如果你以任何方式使用它,请告诉我。

【讨论】:

  • 为连续范围添加了小修复。 1-4 & 5-8 应该连接起来,因为我们只处理整数范围。
猜你喜欢
  • 2015-06-21
  • 1970-01-01
  • 1970-01-01
  • 2014-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-15
  • 2011-09-14
相关资源
最近更新 更多