合并特定列中的多行

合并特定列中的多行

我有一个这样的文件:

xxx ax1 bx1
xxx ax2 bx2 cx2
xxx ax3 bx3
yyy ay1     cy1
zzz az1     cz1
zzz az1 bz2
...

我想要这样的输出:

xxx ax1,ax2,ax3 bx1,bx2,bx3 cx2
yyy ay1                     cy1
zzz az1         bz2         cz1
...

我尝试使用 awk awk -F'\t' -v OFS='\t' '{x=$1;$1="";a[x]=a[x]$0}END{for(x in a)print x,a[x]}' file。但这个命令忽略了它们所属的列,输出不是我想要的。

答案1

awk '
BEGIN{ FS=OFS="\t" }
     { for(i=2; i<=NF;i++)
       if (!seen[$1, $i, i]++)
           grp[$1, i]=(grp[$1, i]==""?"":grp[$1, i] ($i!=""?",":"")) $i
       else
           grp[$1, i]= grp[$1, i]
     }

END{ for(x in grp) {
         split(x, tmp, SUBSEP);
         join[tmp[1]]=(join[tmp[1]]==""?"":join[tmp[1]] OFS) grp[x]
     }
     for (x in join) print x, join[x]
}' infile

输出:

yyy     ay1             cy1
xxx     ax1,ax2,ax3     bx1,bx2,bx3     cx2
zzz     az1     bz2     cz1

将字段括在括号内以检查它们的位置是否正确:

awk '
BEGIN{ FS=OFS="\t" }
     { for(i=2; i<=NF;i++)
       if (!seen[$1, $i, i]++)
           grp[$1, i]=(grp[$1, i]==""?"":grp[$1, i] ($i!=""?",":"")) $i
       else
           grp[$1, i]= grp[$1, i]
     }

END{ for(x in grp) {
         split(x, tmp, SUBSEP);
         join[tmp[1]]=(join[tmp[1]]==""?"":join[tmp[1]] OFS) "["grp[x]"]"
     }
     for (x in join) print x, join[x]
}' infile

输出:

yyy     [ay1]   []      [cy1]
xxx     [ax1,ax2,ax3]   [bx1,bx2,bx3]   [cx2]
zzz     [az1]   [bz2]   [cz1]

笔记:

  • 无论您的输入是否已排序,这个答案都将起作用。
  • 这个答案在输出时会打乱记录。
  • 该代码!seen[$1, $i, i]++用于列中每个 ID 的条目是唯一的。如果您想要删除所有列中的重复条目而不管 ID,请将其更改为!seen[$1, $i]++);

举个例子:

$ cat infile
xxx     ax1     ax1
xxx     ax1     bx2     ax3
xxx     ax3     bx2
yyy     ay1     ay1     cy3
zzz     az1     bz3     cz1
zzz     az1     bz2     bz3

输出(当!seen[$1, $i, i]++);仅删除属于该 ID 的每列中的重复条目:

yyy     [ay1]   [ay1]   [cy3]
xxx     [ax1,ax3]       [ax1,bx2]       [ax3]
zzz     [az1]   [bz3,bz2]       [cz1,bz3]

输出(当seen[$1, $i]++);无论该条目的列位置如何,所有重复的条目都会被删除:

yyy     [ay1]   []      [cy3]
xxx     [ax1]   [bx2]   [ax3]
zzz     [az1]   [bz3,bz2]       [cz1]

答案2

$ cat tst.awk
BEGIN { FS=OFS="\t" }
$1 != prev {
    if ( NR > 1 ) {
        prt()
    }
    prev = $1
}
{
    for ( i=1; i<=NF; i++ ) {
        if ( ($i == "") || (seen[i,$i]++) ) {
            uniq[i]
        }
        else {
            uniq[i] = (uniq[i] == "" ? "" : uniq[i] ",") $i
        }
    }
}
END {
    prt()
}

function prt(   i) {
    for ( i=1; i in uniq; i++ ) {
        printf "%s%s", (i>1 ? OFS : ""), uniq[i]
    }
    print ""
    delete uniq
    delete seen
}

$ awk -f tst.awk file
xxx     ax1,ax2,ax3     bx1,bx2,bx3     cx2
yyy     ay1             cy1
zzz     az1     bz2     cz1

$ awk -f tst.awk file | column -s$'\t' -t
xxx  ax1,ax2,ax3  bx1,bx2,bx3  cx2
yyy  ay1                       cy1
zzz  az1          bz2          cz1

相关内容