【问题标题】:Concatenating multiple rows into single line in pfam output file在 pfam 输出文件中将多行连接成单行
【发布时间】:2016-06-23 06:03:32
【问题描述】:

我在多行中有以下数据:

TRINITY_GG_428_c0_g1_i1_orf1 PF13499.1 EF_hand_5
TRINITY_GG_428_c0_g1_i1_orf1 PF00036.27 efhand
TRINITY_GG_428_c0_g1_i1_orf1 PF13405.1 EF_hand_4
TRINITY_GG_428_c0_g1_i1_orf1 PF13833.1 EF_hand_6
TRINITY_GG_428_c0_g1_i1_orf1 PF13202.1 EF_hand_3
TRINITY_GG_429_c0_g1_i1_orf1 PF00156.22 Pribosyltran
TRINITY_GG_431_c5_g1_i1_orf1 PF00475.13 IGPD
TRINITY_GG_461_c0_g1_i1_orf1 PF01208.12 URO-D
TRINITY_GG_461_c0_g1_i1_orf1 PF12876.2 Cellulase-like

我想做的是将它们转换成一行:

TRINITY_GG_428_c0_g1_i1_orf1 PF13499.1 EF_hand_5 | PF00036.27 efhand | PF13405.1 EF_hand_4 | PF13833.1 EF_hand_6 | PF13202.1 EF_hand_3
TRINITY_GG_429_c0_g1_i1_orf1 PF00156.22 Pribosyltran
TRINITY_GG_431_c5_g1_i1_orf1 PF00475.13 IGPD
TRINITY_GG_461_c0_g1_i1_orf1 PF01208.12 URO-D | PF12876.2 Cellulase-like

匹配的线总是彼此相邻。

如何在 sed/awk/Perl/Python 中解决这个问题?

【问题讨论】:

  • 匹配的行是否总是连续的?
  • 是的,我想将重复的行连接成一行

标签: python perl awk sed


【解决方案1】:

你可以使用 python 正则表达式来做这样的事情

import re

out_lines = []
with open('file.txt', 'r') as f:
    key = None
    key_lines = []
    for line in f:
        m = re.match(r'^(\S+)\s(.+)$', line)
        k, v = m.group(1), m.group(2)
        if k != key:
            if key:
                out_lines.append('{0} {1}'.format(key, ' | '.join(key_lines)))
            key = k
            key_lines = [v]
        else:
            key_lines.append(v)
    else:
        if key:
            out_lines.append('{0} {1}'.format(key, ' | '.join(key_lines)))

with open('out.txt', 'w') as f:
    f.write('\n'.join(out_lines))

【讨论】:

    【解决方案2】:

    使用 GNU sed:

    $ sed -r ':a;N;s/^([^ ]*)( .*)\n\1(.*)$/\1\2 |\3/;ta;P;D' infile
    TRINITY_GG_428_c0_g1_i1_orf1 PF13499.1 EF_hand_5 | PF00036.27 efhand | PF13405.1 EF_hand_4 | PF13833.1 EF_hand_6 | PF13202.1 EF_hand_3
    TRINITY_GG_429_c0_g1_i1_orf1 PF00156.22 Pribosyltran
    TRINITY_GG_431_c5_g1_i1_orf1 PF00475.13 IGPD
    TRINITY_GG_461_c0_g1_i1_orf1 PF01208.12 URO-D | PF12876.2 Cellulase-like
    

    主要部分是替换:它检查两行是否以相同的字符串开头(直到第一个空格),如果是,则连接这些行,从第二行中删除字符串并用管道替换换行符.

    分开:

    :label     # Label to branch to
    N          # Append next line to pattern space
    s/^([^ ]*)( .*)\n\1(.*)$/\1\2 |\3/  # Substitution
    t label    # Branch to label if the substitution took place
    P          # Strings weren't identical: print up to newline
    D          # Delete up to newline, start new cycle (second line become first line)
    

    要使这项工作与 BSD sed 一起工作,我们必须围绕标签拆分命令并使用 -E 标志而不是 -r

    sed -E -e ':a' -e 'N;s/^([^ ]*)( .*)\n\1(.*)$/\1\2 |\3/;ta' -e 'P;D' infile
    

    为了更好的衡量,仔细看看替换:

    s/            # Start substitution
        ^         # Anchor at start of pattern space
        ([^ ]*)   # Match and capture non-space characters (group #1)
        ( .*)     # Capture up to end of line (group #2)
        \n        # Match newline
        \1        # Start of second line: match first capture group
        (.*)      # Capture rest of second line (group #3)
        $         # Anchor at end of pattern space
    /             # Delimiter for substitution
        \1\2 |\3  # Substitute: captures groups 1 and 2, space, pipe, capture group 3
    /             # End of substitution
    

    【讨论】:

      【解决方案3】:

      这是一种非常常见的编程模式。您需要使用 Perl 哈希来累积属于每个不同初始字段(键)的所有数据。那么只需要按照需要的顺序和格式打印hash

      这个程序演示。我假设您希望按键的词汇顺序打印键。如果您需要任何不同的内容,例如它们在源数据中首次出现的顺序,那么请说出来——需要做一些小改动

      该程序期望输入文件的路径作为命令行参数,并将其输出发送到 STDOUT,STDOUT 可能会以正常方式重定向

      use strict;
      use warnings 'all';
      
      my %data;
      
      while ( <> ) {
          chomp;
          my ($key, $val) = split ' ', $_, 2;
          push @{ $data{$key} }, $val;
      }
      
      print $_, ' ', join(' | ', @{ $data{$_} }), "\n" for sort keys %data;
      

      输出

      TRINITY_GG_428_c0_g1_i1_orf1 PF13499.1 EF_hand_5 | PF00036.27 efhand | PF13405.1 EF_hand_4 | PF13833.1 EF_hand_6 | PF13202.1 EF_hand_3
      TRINITY_GG_429_c0_g1_i1_orf1 PF00156.22 Pribosyltran
      TRINITY_GG_431_c5_g1_i1_orf1 PF00475.13 IGPD
      TRINITY_GG_461_c0_g1_i1_orf1 PF01208.12 URO-D | PF12876.2 Cellulase-like
      

      【讨论】:

        【解决方案4】:

        在当前行的第一个字段与上一行相同的情况下建立所有行的连接记录,然后在第一个字段的值发生变化时打印它:

        $ awk '
            $1==prev { rec = rec " | " $2 " " $3 }
            $1!=prev { if (NR>1) print rec; rec=$0 }
            { prev=$1 }
            END { print rec }
        ' file
        TRINITY_GG_428_c0_g1_i1_orf1 PF13499.1 EF_hand_5 | PF00036.27 efhand | PF13405.1 EF_hand_4 | PF13833.1 EF_hand_6 | PF13202.1 EF_hand_3
        TRINITY_GG_429_c0_g1_i1_orf1 PF00156.22 Pribosyltran
        TRINITY_GG_431_c5_g1_i1_orf1 PF00475.13 IGPD
        TRINITY_GG_461_c0_g1_i1_orf1 PF01208.12 URO-D | PF12876.2 Cellulase-like
        

        或者,如果您的输入行键不连续并且您不关心输出顺序与输入顺序相同并且您的输入文件足够小以将其全部保存在内存中,那么您可以使用建议的哈希方法在另一个答案中:

        $ awk '{a[$1]=($1 in a ? a[$1]" | "$2" "$3 : $0)} END{for (k in a) print a[k]}' file
        TRINITY_GG_429_c0_g1_i1_orf1 PF00156.22 Pribosyltran
        TRINITY_GG_461_c0_g1_i1_orf1 PF01208.12 URO-D | PF12876.2 Cellulase-like
        TRINITY_GG_431_c5_g1_i1_orf1 PF00475.13 IGPD
        TRINITY_GG_428_c0_g1_i1_orf1 PF13499.1 EF_hand_5 | PF00036.27 efhand | PF13405.1 EF_hand_4 | PF13833.1 EF_hand_6 | PF13202.1 EF_hand_3
        

        【讨论】:

        • 像这样将 a[$1] 替换为 $0 是否可以:awk '{a[$1]=($1 in a ? $0" | "$2" "$3 : $0)} END{for (k in a) print a[k]}' file?
        • 不确定我是否理解这个问题,但a[$1]=($1 in a ? $0" | "$2" "$3 : $0) 最好写成a[$1]=$0 ($1 in a ? " | "$2" "$3 : ""),因此您只需说明公共部分 $0,一次并明确表示它是无条件发生的。跨度>
        猜你喜欢
        • 2012-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-12
        • 1970-01-01
        • 1970-01-01
        • 2016-01-03
        相关资源
        最近更新 更多