【问题标题】:Removing lines with repetitive values in last删除最后一个重复值的行
【发布时间】:2012-11-07 12:34:30
【问题描述】:

我有一个制表符分隔的文件,看起来像这样

chr1  12226559  12227059  TNFRSF1B       
chr1  17051560  17052060                 
chr1  17053279  17053779                 
chr1  17338423  17338923  ATP13A2        
                          ATP13A2        
                          ATP13A2        
chr1  19577574  19578074  EMC1           
                          MRTO4          
chr1  19578046  19578546  EMC1           
                          MRTO4          
chr1  19638239  19638739  AKR7A2         
                          PQLC2          
                          PQLC2          
                          PQLC2
                          AKR7A2         
                          PQLC2     

我希望删除 column4 值重复的行。

前三列是坐标,在这些坐标中列出了我们找到的任何内容(在 col4 中),对于每个坐标,我只想有唯一的名称而不是重复的名称。

我想要这样的输出

chr1  12226559  12227059  TNFRSF1B       
chr1  17051560  17052060                 
chr1  17053279  17053779                 
chr1  17338423  17338923  ATP13A2              
chr1  19577574  19578074  EMC1           
                          MRTO4          
chr1  19578046  19578546  EMC1           
                          MRTO4          
chr1  19638239  19638739  AKR7A2         
                          PQLC2 

我尝试过的事情

sort -k 4 -u file

awk '{if($4==temp1){next;}else{print}temp1=$4}' file

没有用 :(

请帮忙

谢谢

【问题讨论】:

  • 如果您将字段分隔符更改为制表符,我确信您的 awk 脚本会起作用。否则 awk 会在空白处分隔单词。
  • +1 用于示例数据、所需输出和解决方案尝试。祝你好运。
  • +1 我花了很长时间解决你的挑战 ;-)
  • @Angelo @dogbane 考虑 3 行:第一行是空的,最后是 AKR7A2;第二个填写chr1 19638239 19638739 AKR7A2;第三个与第一个相同。应该保留的线是什么?根据您的问题要求,我们应该保留第一个(空的)。但我的感觉会保留第二个(填充的)。请发表您的意见;-) 干杯
  • 亲爱的,正如 dogbane 所建议的,我已经在输入文件中进行了编辑。请尝试使用新的输入示例文件。谢谢。

标签: python perl bash awk bioinformatics


【解决方案1】:

你只需要

awk '$NF != prev {print} {prev=$NF}'

编辑:处理新的输入

awk '{
    if (NF == 1) 
        value = $1
    else {
        key =  $1 SUBSEP $2 SUBSEP $3
        value = $4
    }
    if ((key SUBSEP value) in val) 
        next
    print
    val[key, value] = 1
}' input

【讨论】:

  • 如果您喜欢打码高尔夫,您可以利用默认操作(打印):awk '$NF!=p;{p=$NF}'
  • @glenn jackman 请看一下不同的输入,我确实更改了输入。
  • 好主意,很干净 ;-) 但是当处理没有第四列的两个连续行并以相同的第三列结尾 17053779 时,第二行将被删除...如何保留行不有第四列?请提供第二个漂亮的脚本来解决这个问题。干杯;-)
【解决方案2】:
sed '1{x;d};H;x;s/\([ ][^\n ]*\)[ ]*\n[ ]*\1[ ]*\n/\1\n/;$p;x;d;$p;x;d' FILE

如果您的文件中有标签,除了空格,您可以将所有[ ] 替换为[[:space:]]

【讨论】:

  • +1 仅使用sed 脚本就可以应对这一挑战,表现非常出色!恭喜;-)
  • 我还写了一个sed解释器【语义未完,只有语法】,以后会集成到gnu/Emacs中:lists.gnu.org/archive/html/emacs-devel/2011-05/msg00745.html
  • 真可惜I do not use emacs 在我的新工作中...我希望您的 lisp 脚本将包含在正式版本中...祝您好运 ;-) PS:您仍然使用 voila.fr! (请在您的回答中接受我的编辑)
【解决方案3】:

简单的awk 脚本

awk -F'\t' '{OFS="\t"; if ($4=="" || $4!=old) print; old=$4}' input.txt

结果

chr1    12226559        12227059        TNFRSF1B
chr1    17051560        17052060
chr1    17053279        17053779
chr1    17338423        17338923        ATP13A2
chr1    19577574        19578074        EMC1
                                        MRTO4
chr1    19578046        19578546        EMC1
                                        MRTO4
chr1    19638239        19638739        AKR7A2
                                        PQLC2

清洁

为了准备我的input.txt 文件,我复制了问题中的文本。但我不得不用制表符替换空格。因此我使用了sed 命令。我还注意到一些尾随空格(在行尾)。最后,我使用了下面的sed 命令来清理输入文件:

sed 's/ *$//;/^[^ ]/s/  */\t/g;/^ /s/  */\t\t\t/g;' copy-fron-so.txt > input.txt

来自@dogbane 评论的输入文件

chr1  12226559  12227059  TNFRSF1B
chr1  17051560  17052060
chr1  17053279  17053779
chr1  17338423  17338923  ATP13A2
                          ATP13A2
                          ATP13A2
chr1  19577574  19578074  EMC1
                          MRTO4
chr1  19578046  19578546  EMC1
                          MRTO4
chr1  19638239  19638739  AKR7A2
                          PQLC2
                          PQLC2
                          PQLC2
                          AKR7A2

(最后一行已添加)

清洗处理

$> sed 's/ *$//;/^[^ ]/s/  */\t/g;/^ /s/  */\t\t\t/g;' copypaste.txt > input.txt
$> awk -F'\t' '{OFS="\t"; if ($4=="" || $4!=old) print; old=$4}' input.txt
chr1    12226559        12227059        TNFRSF1B
chr1    17051560        17052060
chr1    17053279        17053779
chr1    17338423        17338923        ATP13A2
chr1    19577574        19578074        EMC1
                                        MRTO4
chr1    19578046        19578546        EMC1
                                        MRTO4
chr1    19638239        19638739        AKR7A2
                                        PQLC2
                                        AKR7A2

需求变化

不应打印具有AKR7A2 的最后一行。因此,我们首先需要对input.txt 文件进行排序。注意选项-t 用于引入选项卡,在bashvi 上按[CTRL-V],然后按[TAB](在此选项卡周围加上引号)。

$> LANG=C sort -k 4 -s -t ' ' input.txt > sorted.txt 
$> awk -F'\t' '{OFS="\t"; if ($4=="" || $4!=old) print; old=$4}' sorted.txt
chr1    17051560        17052060
chr1    17053279        17053779
chr1    19638239        19638739        AKR7A2
chr1    17338423        17338923        ATP13A2
chr1    19577574        19578074        EMC1
                                        MRTO4
                                        PQLC2
chr1    12226559        12227059        TNFRSF1B

请注意,现在有一行以MRTO4 结尾!

【讨论】:

  • 确定这个 awk 命令或者您是否使用了正确的输入。这个带有我的示例输入的脚本不会给出这个输出。 :(
  • 是的,我复制粘贴了您的输入并进行了一些清理:删除连续空格,用制表符替换它们,删除尾随空格......我在回答中附加了我为清理输入文件所做的事情。 ..
  • 请@Angelo,试试我的清理sed 脚本。也许你的输入文件中有一些讨厌的空间......我已经用清理过的输入文件测试了我的脚本,它可以工作。让我知道...干杯
  • 嗨@dogbane。第四列不需要排序。看看结果输出,有两行以MRTO4 结尾。正如问题示例中所指定的那样,这是正确的,不是吗?
  • @olibre 我的意思是您的代码假定第四列在每个“chr1”块中排序。例如,尝试在输入文件的末尾添加另一个AKR7A2。然后,您的脚本将打印出两个 AKR7A2 而不是一个。
【解决方案4】:

也许以下会有所帮助:

use strict;
use warnings;

my %seen;

while (<DATA>) {
    my ($col3) = (split)[-1];
    print if !$seen{$col3}++ or !$col3;
}

__DATA__
chr1    12226559    12227059    TNFRSF1B
chr1    17051560    17052060    
chr1    17053279    17053779    
chr1    17338423    17338923    ATP13A2
                                ATP13A2
                                ATP13A2
chr1    19577574    19578074    EMC1
                                MRTO4
chr1    19578046    19578546    EMC1
                                MRTO4
chr1    19638239    19638739    AKR7A2
                                PQLC2
                                PQLC2
                                PQLC2

输出:

chr1    12226559    12227059    TNFRSF1B
chr1    17051560    17052060    
chr1    17053279    17053779    
chr1    17338423    17338923    ATP13A2
chr1    19577574    19578074    EMC1
                                MRTO4
chr1    19638239    19638739    AKR7A2
                                PQLC2

这个输出也可以用下面的单行来实现:

perl -ane "print if !$X{$F[-1]}++ or !$F[-1]" data.txt

【讨论】:

  • 嗨@Kenosis 我没有去测试你的perl 脚本。我使用perl v5.8.8。在第一个脚本上,我有这个输出:Name "main::DATA" used only once: possible typo at -e line 6.\n readline() on unopened filehandle DATA at -e line 6.。在第二个小脚本上,此输出:syntax error at -e line 1, near "++ or"\n Execution of -e aborted due to compilation errors.。请你能提供更多信息如何解决这个问题吗?干杯;-)
【解决方案5】:

使用一个很小的perl 脚本:

perl -e 'my $col4 = "";
while (<>) {
  chomp;
  my @f = split(/\t/, $_);
  if ($f[3] eq "" || $f[3] ne $col4) {
    print $_, "\n";
  }
  $col4 = $f[3];
}' input.txt

结果:

chr1    12226559        12227059        TNFRSF1B
chr1    17051560        17052060
chr1    17053279        17053779
chr1    17338423        17338923        ATP13A2
chr1    19577574        19578074        EMC1
                                        MRTO4
chr1    19578046        19578546        EMC1
                                        MRTO4
chr1    19638239        19638739        AKR7A2
                                        PQLC2

【讨论】:

  • 请在打印中添加\n,否则所有输出都打印在一行中。此外,perl 脚本打印两个以ATP13A2 结尾的连续行。最后,脚本仅打印具有空第四列的连续行中的第一行(都应该打印),与uniq -f4 相同的缺点。祝你更正好运...
  • +1 完美,您的脚本符合问题要求。但是,@dogbane 添加了另一个要求 => 请从我的答案中间阅读...干杯 ;-)
【解决方案6】:

鉴于我将使用的新发布的输入:

gawk -F'\t' '!/^\t/{delete a} !a[$4]++' file

我使用 gawk,所以我可以一次清楚地删除整个数组,而其他 awk 使用不太清晰的:

awk -F'\t' '!/^\t/{split("",a)} !a[$4]++' file

【讨论】:

    【解决方案7】:

    如果重复的行在 all 列中重复,而不仅仅是第四列,uniq(1) 可能是合适的。尝试仅运行 uniq file 并查看输出是否符合您的预期。

    【讨论】:

    • 好吧,我希望该命令仅在一个片段中查找重复,例如此片段中的重复 chr1 17338423 17338923 ATP13A2 ATP13A2 ATP13A2
    猜你喜欢
    • 2022-11-21
    • 1970-01-01
    • 2018-11-06
    • 2023-03-31
    • 2021-12-26
    • 1970-01-01
    • 2017-06-10
    • 1970-01-01
    • 2019-08-08
    相关资源
    最近更新 更多