在文件中比较然后在文件之间进行比较

时间:2016-03-02 05:25:01

标签: perl

文件1:

col1    col2     col3   col4   col5    col6     col7    col8       
chr1    1361651 1361652 1       3       0       0       1

chr1    1421915 1421916 1       1       1       0       0

chr1    3329147 3329148 2       2,3     0       1       1

chr1    8421092 8421093 3       1,2,3   1       1       1

chr1    13802362        13802363        3       1,2,3   1       1       1

chr1    43315088        43315089        2       1,2     1       1       0

chr1    52256664        52256665        2       1,3     1       0       1

文件2:

col1      col2       col3     col4     col5    col6     col7.....col16

chr1    1361651     1361652    G       data5   data6    data7....data16

chr1    2468066     2468067   G       data5   data6    data7....data16

chr1    3329147     3329148   ........

chr1    8421092     8421093   ........ 

chr1    13802362    13802363   ........        

chr1    43315088    43315089   ........        

chr1    52256664    52256665   ........

Output.txt的

检查文件1的第5列是否有1,2,3然后比较文件1和2之间的第1列和第2列并在单独的文件中打印匹配

col1      col2       col3     col4     col5    col6     col7.....col16

chr1    8421092     8421093   ........ 

chr1    13802362    13802363   ........        

我的代码可以帮助我比较两个文件,但首先我需要在文件中进行比较,然后在整个文件中进行比较。

my $file1 = $ARGV[0];
my $file2 = $ARGV[1];
open(FILE1, $file1);
open(FILE2, $file2);
open my $f, '>', "output.txt" or die "Cannot open output.txt: $!";
my @arr1=<FILE1>;
my @arr2=<FILE2>;
close FILE1;
close FILE2;
for (@arr1)
{
    chomp;
    my($hit1,$hit2,$hit3,$hit4,$hit5,$rest)=split(/\t/);
    my $ckey="$hit1\_$hit2";
    $chash{$ckey}=1;
}
for (@arr2)
{
    chomp;
    my($val1,$val2,$val3,$val4,$val5,$rest)=split(/\t/);
    my $ckey="$val1\_$val2";
    $chash{$ckey}++;
    if( $chash{$ckey} == 2 )
    {
    # this key has been seen in both previous files
    print $f "$_\n";
    }
}

2 个答案:

答案 0 :(得分:0)

您的描述有点含糊不清 - 暂时不要检查“1,2,3” - 您的描述会讨论比较第1列和第2列,但第1列的内容相同两个文件中的每一行 - 即“chr”。正如您在第2列和第2列中突出显示的数字一样。 3,当它们出现在“Output.txt”文件中时,我认为你的意思是那两列不是1和2 - 这就是我正在进行的基础。

在转向解决方案之前,我只想强调一下现有代码的几个问题 - 首先,你是字符串连接两列。如果第2列和第2列怎么办? 3有“46”&amp; “123”分别在一个文件中;在另一个“461”&amp; “23”,然后你的concat会给你一个错误的比赛。现在也许,只是“不会发生”如果你知道你的数据那么好,那么公平 - 但你需要意识到这种可能性。

更重要的是,对先前看到的数字的哈希跟踪不足以完成您需要的任务 - 如果第2列和第2列中有两条具有相同内容的行,会发生什么? 3 在同一个文件中?如果一个文件中有两行相同,而另一个文件中有一行相同,会产生总共3行,但您只能查找一个理货2?。同样,您可能知道这些组合不会出现在您的数据中,但您需要了解潜伏的错误。

另一件事 - 如果第2列和第2列的匹配,我(至少对我来说)并不清楚3必须分别位于每个文件的同一行。在您的测试数据中,第2列和第2列第4行和第4行5是匹配线4&amp; 5分别在另一个文件中 - 是必要的吗?或者,(再次,将“1,2,3”的东西搁置一分钟)可以将第2列和第2列放在一起。在第一个文件的第4行3上愉快地匹配第2列和第2列第二行第3行中的第3个?

我并不是说在这里很难,但显然这些事情与找到正确的解决方案非常相关。

如果您希望对现有代码进行极简主义更改,因为我指出的这些内容都不重要,您需要做的只是“拯救”第一个循环,除非“1,2 ,3“位于第5列,即$arr1[4]或 - 分割后 - $hit5。好吧,只需添加一下;

chomp;
my($hit1,$hit2,$hit3,$hit4,$hit5,$rest)=split(/\t/);
next unless $hit4 eq "1,2,3";   # <-- Added line
my $ckey="$hit1$hit2";
$chash{$ckey}=1; 

'next'会立即终止当前的循环播放,因此$chash不会使用第2列和第2列的内容进行更新。 3 - 但是,我必须重复一遍,最终结果是非常不稳定的代码。

以下是另一种实施方式:

#!/usr/bin/env perl
use v5.12;

my $file1 = $ARGV[0];
my $file2 = $ARGV[1];
open(FILE1, $file1) or die "$file1: $!\n";
open(FILE2, $file2) or die "$file2: $!\n";
open my $f, '>', "output.txt" or die "Cannot open output.txt: $!";

my @arr1 = map [split(" ", $_)], <FILE1>;
my @arr2 = map [split(" ", $_)], <FILE2>;
close FILE1;
close FILE2;

my $i = 0;
for my $arr1row (@arr1) {
    # Grab the same row in file 2
    my $arr2row = $arr2[$i++] ;

    # bail unless we have "1,2,3" in col 5
    next unless $arr1row->[4] eq "1,2,3" ;

    # bail if we dont have a line from file 2 because its shorter
    next unless defined $arr2row ;

    # If col2 and col3 are the same from each file ...
    if ($arr1row->[1] == $arr2row->[1] &&
        $arr1row->[2] == $arr2row->[2] )  {

        # print out all fields from file 2
        say $f join("\t", @$arr2row);
    }
}

答案 1 :(得分:0)

显示的代码有点太复杂了。此外,如果文件之间的任何单词恰好相同,则不清楚哈希将如何处理。此外,还需要保持整行与匹配位置的坐标。您需要额外的数据结构。这是一种更简单的方法。

加入每行的前两个字段并将该字符串放在数组上。通过file1时也检查条件,如果没有找到退出。为file2形成和存储相同的字符串,同时存储整行。然后遍历任一数组的索引,并在字符串匹配时选择相应的file2行(每个需求)。这些线是我们的输出。代码可以更简单,请参阅 Notes

use warnings;
use strict;

my $patt = '1,2,3';

# Join cols 1,2 into a string, store; check condition
open my $fh1, '<', 'file1.txt';
my @f1;
my $go = 0;
while (my $line = <$fh1>) {
    next if $line =~ /^\s*$/;
    my @cols = split '\s+', $line;
    my ($c1, $c2) = @cols[0,1];
    next if not $c1 or not $c2;
    push @f1, join '_', $c1, $c2;
    $go = 1 if $cols[4] and $patt eq $cols[4];
}
close $fh1;

if (not $go) {
    print "Condition not satisfied, exiting.\n";    
    exit 0;
}

# Join cols 1, 2 from file2, store; store lines
my (@f2, @lines);
open my $fh2, '<', 'file2.txt';
while (<$fh2>) {
    next if /^\s*$/;
    my ($c1, $c2) = (split)[0,1];
    next if not $c1 or not $c2;
    push @f2, join('_', $c1, $c2);
    push @lines, $_;
}
close $fh2;

# Find matches: compare strings from arrays
# Print corresponding lines file2
my @output;
foreach my $i (0..$#f2) {
    push(@output, $lines[$i]) if $f1[$i] eq $f2[$i];
} 
print "$_\n" for @output;

注意即可。通过问题描述,两个样本文件的大多数行匹配,具有相等的前两个字段。显示的预期输出不同意这一点,但描述如果相当明确。手动删除空格的额外空行,打印


    col1       col2        col3   col4      col5    col6     col7    col8       
    chr1    1361651     1361652    G       data5   data6    data7....data16
    chr1    3329147     3329148   ........
    chr1    8421092     8421093   ........ 
    chr1    13802362    13802363   ........        
    chr1    43315088    43315089   ........        
    chr1    52256664    52256665   ........

备注的。仅仅比较字段可以加入;具有可识别的序列(这里只是_)允许我们在需要时恢复它们。明确做出了一些合理的假设:文件长度相同,结构相同(缺少相同的列)。如果他们不坚持,很容易调整这个逐步处理。在阅读我们防范的文件时:前两个字段丢失,缺少第四列。如果肯定不需要

while (<$fh1>) {
    next if /^\s*$/;
    my ($c1, $c2, $c4) = (split)[0,1,4];
    push @f1, join '_', $c1, $c2;
    $go = 1 if $patt eq $cols[4];
}
exit if not $go;
while (<$fh2>) {
    next if /^\s*$/;
    push @f2, join '_', (split)[0,1];
    push @lines, $_;
}
@output = map { $lines[$_] } grep { $f1[$_] eq $f2[$_] } (0..$#f2);