1

我有一个 12 列的 txt 文件。有些行是重复的,有些则不是。例如,我复制到数据的前 4 列。

0       0       chr12   48548073  
0       0       chr13   80612840
2       0       chrX    4000600 
2       0       chrX    31882528 
3       0       chrX    3468481 
4       0       chrX    31882726
4       0       chr3    75007624

根据第一列,您可以看到除了条目“3”之外还有一些重复项。我想打印唯一的单个条目,在本例中为“3”。

输出将是

3       0       chrX    3468481

有没有使用 awk 或 perl 的快速方法?我只能考虑在 perl 中使用 for 循环,但考虑到我有大约 150 万个条目,这可能需要一些时间。

4

5 回答 5

4

试试这个 awk 单线:

awk '{a[$1]++;b[$1]=$0}END{for(x in a)if(a[x]==1)print b[x]}' file
于 2013-07-10T12:07:01.520 回答
3

这是另一种方式:

uniq -uw8 inputFile
  • -w8将比较前8 个字符(即您的第一列)的唯一性。
  • -u选项将只打印出现一次的行。

测试:

$ cat file
0       0       chr12   48548073  
0       0       chr13   80612840
2       0       chrX    4000600 
2       0       chrX    31882528 
3       0       chrX    3468481 
4       0       chrX    31882726
4       0       chr3    75007624

$ uniq -uw8 file
3       0       chrX    3468481 
于 2013-07-10T13:26:44.683 回答
2

不是单行的,但这个小的 Perl 脚本完成了同样的任务:

#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';

# get filehandle
open( my $fh, '<', 'test.txt');

# all lines from your file
my %line_map; 

while( my $line = <$fh> ) { # read a line

   my $key;
   my @values;

   # split on whitespace
   ($key, @values) = split(/\s+/, $line);

   # delete a line if it already exists in the map
   if( exists $line_map{$key} ) {
       delete $line_map{$key};
   } 
   else { # mark a line to show that it has been seen
      $line_map{$key} = join("\t", @values);
   }
}

# now the map should only contain non-duplicates
for my $k ( keys %line_map ) {
   print "$k\t", $line_map{$k}, "\n"; 
}
于 2013-07-10T13:11:21.180 回答
1

无法正确格式化评论。@JS웃 可能依赖于 GNU uniq...这似乎适用于 BSD 派生版本:

grep ^`cut -d" " -f1 col_data.txt  | uniq -u` file.txt

必须有一个更简短的perl答案:-)

于 2013-07-10T20:33:53.527 回答
0

我知道必须有一个perl单一的回应。在这里 - 没有经过严格测试,所以请注意购买者 ;-)

perl -anE 'push @AoA,[@F]; $S{$_}++ for @F[0];}{for $i (0..$#AoA) {for $j (grep {$S{$_}==1} keys %S) {say "@{$AoA[$i]}" if @{$AoA[$i]}[0]==$j}}' data.txt

这种方法的缺点是它以稍微修改的格式输出数据(我认为这很容易修复)并且它使用两个for循环和一个“蝴蝶运算符”(!!)它还使用grep()(它引入了一个隐式循环- 即即使您不必自己编写循环也可以运行代码),因此它可能会很慢,有 150 万条记录。我希望看到它与awk虽然相比uniq

从好的方面来说,它不使用任何模块,应该可以在 Windows 和 OSX 上运行。当有几十个具有唯一第一列的相似记录并且不需要在检查唯一行之前对输入进行排序时,它就可以工作。该解决方案主要摘自Joseph Hall、Joh McAdams 和brian d foy在Effective Perl Programming结尾处的单行示例 (一本很棒的书——当聪明的比赛和尘埃落定时,我希望新版本出现):~~given when

这是(我认为)它的工作原理:

  • 因为我们正在使用-a我们免费获得@F数组所以使用它而不是拆分
  • 因为我们使用的是-n我们在一个while() {}循环中,所以intopush的元素作为匿名引用数组(充当“匿名数组构造函数”)。这样他们就会闲逛,我们以后可以参考他们(这甚至有意义吗???)@F@AoA[]
  • 使用上面提到的书中的$seen{$_}++习语(我们使用$S而不是$seen),并由@Axeman 在这里对 SO进行了很好的描述@F[0]%S根据我们看到一个元素的次数(或行)具有给定值(行内容)。
  • 使用“蝴蝶”}{突破while然后,在一个单独的块中,我们使用两个for循环遍历外部数组并检查每个元素(它们本身是匿名数组$i- 每行一个),然后,对于每个内部匿名数组,在我们之前创建的散列(或内部循环)中与等于“1”的grep值一起使用,并将这些值连续放入.keys%Sfor $j (grep {$S{$_}==1} keys %S)$j
  • 最后,我们遍历外部数组并打印任何匿名数组,其中该数组的第一个元素等于每个 ( $j) 的值。我们这样做:(@{$AoA[$i]}[0]==$j)。

awk在@Kent 的手中更简洁一些。如果有人对如何缩短或记录我的“线路噪音”有任何建议(我从来没有说过perl!)请添加建设性意见!

谢谢阅读。

于 2013-07-14T20:59:27.830 回答