【问题标题】:Remove lines which are substrings of other lines删除作为其他行子字符串的行
【发布时间】:2016-06-04 15:31:22
【问题描述】:

如何删除作为文件中其他行的子字符串的行,同时保留包含它们的较长字符串?

我有一个文件,其中包含作为字符串的肽序列 - 每行一个序列字符串。我想保留包含所有序列的字符串并删除所有作为文件中其他行的子字符串的行。

输入

GSAAQQYW
ATFYGGSDASGT
GSAAQQYWTPANATFYGGSDASGT
GSAAQQYWTPANATF
ATFYGGSDASGT
NYARTTCRRTG
IVPVNYARTTCRRTGGIRFTITGHDYFDN
RFTITGHDYFDN
IVPVNYARTTCRRTG
ARTTCRRTGGIRFTITG

预期输出

GSAAQQYWTPANATFYGGSDASGT
IVPVNYARTTCRRTGGIRFTITGHDYFDN

输出应该只保留最长的字符串,并删除所有作为最长字符串的子字符串的行。因此,在上面的输入中,第 1、2、4 和 5 行是第 3 行的子字符串,因此输出保留的第 3 行。类似地,第 6、8、9 和 10 行的字符串都是第 7 行的子字符串,因此行7 被保留并写入输出。

【问题讨论】:

  • 什么是“更长”? “最长的两个”?
  • 啊,你想删除任何作为另一个字符串的子字符串的字符串吗?到目前为止,您尝试过什么?
  • 是的,我的意思是两个最长的字符串。
  • 抱歉给您带来了困惑。我只是进行了编辑以使其清楚。希望它现在清楚。此外,由于这些是肽序列,我将其转换为 fasta 文件并使用 CD-HIT 程序,该程序将具有 100% 同一性的相似序列聚类并产生输出。稍后,将该 fasta 转换为文本文件以供进一步分析。

标签: string perl awk sed substring


【解决方案1】:

也许:

input=./input_file
while read -r str
do
[[ $(grep -c "$str" "$input") == 1 ]] && echo $str
done < "$input"

产生:

GSAAQQYWTPANATFYGGSDASGT
IVPVNYARTTCRRTGGIRFTITGHDYFDN

它很慢 - 但很简单..

【讨论】:

    【解决方案2】:

    这应该做你想做的:

    $ cat tst.awk
    { arr[$0]; strs=strs $0 RS }
    END {
        for (str in arr) {
            if ( split(strs,tmp,str) == 2 ) {
                print str
            }
        }
    }
    
    $ awk -f tst.awk file
    IVPVNYARTTCRRTGGIRFTITGHDYFDN
    GSAAQQYWTPANATFYGGSDASGT
    

    它遍历 arr 中的每个字符串,然后将其用作 split() 的分隔符值 - 如果字符串出现一次,则整个文件内容将被分成两半,因此 split() 将返回 2 但如果字符串是其他字符串的子集,则文件内容将被拆分为多个段,因此拆分将返回大于 2 的数字。

    如果一个字符串可以在输入中出现多次,并且您希望它在输出中多次打印(请参阅下面@G.Cito 评论中的问题),那么您可以将上面的内容修改为:

    !cnt[$0]++ { strs=strs $0 RS }
    END {
        for (str in cnt) {
            if ( split(strs,tmp,str) == 2 ) {
                for (i=1;i<=cnt[str];i++) {
                    print str
                }
            }
        }
    }
    

    【讨论】:

    • 我在底部添加了解释
    • @EdMorton so of all the strings in file, awk 只查找和打印那些可以一分为二的字符串(跳过那些不能被分割的或多次分割的)。 ++ 又好又简单!有没有一种简单的方法来处理“长”字符串(我在下面更巴洛克式的 perl 解决方案中称为“主字符串”)多次出现的情况?使用您的 awk 脚本和 perl %uniq 哈希,它将被排除在输出之外。
    • 如果要求只打印一次,那么您只需将第一行更改为!arr[$0]++{ strs=strs $0 RS }(习惯上arr 将被命名为seencount 在该上下文中使用时) 所以它只在 strs 字符串中出现一次,稍后会被拆分。如果要求打印它与输入中出现的次数一样多,那么您还需要将print str 更改为for (i=1;i&lt;=arr[str];i++) print str。我更新了我的答案以表明这一点。
    【解决方案3】:

    作为 perl 的“单行”(这应该适用于剪切和粘贴到终端):

    perl -E 'chomp(@r=<>); 
            for $i (0..$#r){ 
               map { $uniq{$_}++ if ( index( $r[$i], $_ ) != -1 ) } @r; 
            }
            for (sort keys %uniq){ say if ( $uniq{$_} == 1 ); }' peptide_seq.txt
    
    • 我们从 STDIN (&lt;&gt;) 读取文件 (peptide_seq.txt) 并将其保存在 @r 中,这将是一个数组,其中每个元素都是文件中每一行的字符串。

    • 1234563每个value 是一个数字,当发现一行是另一行的子字符串时递增。使用index,我们可以check whether a string contains a sub-string 并增加相应的哈希值if index() 不会返回“未找到”的值(-1)。
    • “主”字符串包含所有其他字符串作为它们自己的子字符串,并且只会增加一次,因此我们再次循环以打印具有值 == 1%uniq 哈希的键。第二个循环可能是map

      map { say if ( $uniq{$_} == 1 ) } sort keys uniq ;

    作为一个独立的脚本,可以:

    #!perl -l
    chomp(@r=<DATA>); 
    
    for $i (0..$#r) {
      map { $uniq{$_}++ if ( index( $r[$i], $_ ) != -1 ) } @r ;
    }
    
    map { print if ($uniq{$_} == 1) } sort keys %uniq ; 
    
    __DATA__
    GSAAQQYW
    ATFYGGSDASGT
    GSAAQQYWTPANATFYGGSDASGT
    GSAAQQYWTPANATF
    ATFYGGSDASGT
    NYARTTCRRTG
    IVPVNYARTTCRRTGGIRFTITGHDYFDN
    RFTITGHDYFDN
    IVPVNYARTTCRRTG
    ARTTCRRTGGIRFTITG
    

    输出

    GSAAQQYWTPANATFYGGSDASGT
    IVPVNYARTTCRRTGGIRFTITGHDYFDN
    

    【讨论】:

      【解决方案4】:

      这将帮助您满足您的真正需要:

      awk '{ 打印长度(), NR, $0 | “排序-rn”}' sed_longer.txt |头 -n 2

      【讨论】:

        猜你喜欢
        • 2012-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-04-12
        • 2019-05-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多