将共享匹配的列转换为行

将共享匹配的列转换为行

我有一个具有以下结构的文件:

Locus7625186 GO0004866
Locus7625186 GO0010951
Locus7625186 GO0005615
Locus7625186 GO0016021
Locus7360093 GO0004712
Locus7360093 GO0007093
Locus1507198 GO0044212
Locus1507198 GO0045944
Locus1507198 GO0005634
Locus1507198 GO0036464
Locus1507198 GO0046982

我需要将其转换为这样:

Locus7625186 GO0004866 GO0010951 GO0005615 GO0016021
Locus7360093 GO0004712 GO0007093
Locus1507198 GO0044212 GO0045944 GO0005634 GO0036464 GO0046982

GOxxxxxxx请注意,共享相同第一列匹配的数量会有所不同。

答案1

GNU 数据整合

$ datamash -W groupby 1 collapse 2 < file | sed 's/,/ /g'
Locus7625186    GO0004866 GO0010951 GO0005615 GO0016021
Locus7360093    GO0004712 GO0007093
Locus1507198    GO0044212 GO0045944 GO0005634 GO0036464 GO0046982

(如果您不介意默认的逗号分隔符,您可以省略 sed 中的管道)。

答案2

您可以使用GNU sed流编辑器来解决此问题:

sed -Ee '
   :a
      $!N
      s/^((\S+)\s.*)\n\2(\s.*)/\1\3/
   ta
   P;D
' file

结果

Locus7625186 GO0004866 GO0010951 GO0005615 GO0016021
Locus7360093 GO0004712 GO0007093
Locus1507198 GO0044212 GO0045944 GO0005634 GO0036464 GO0046982

这也可以使用以下方法完成POSIX sed

sed -e '
   :a
      $!N
      s/^\(\([^[:space:]]\{1,\}\)[[:space:]].*\)\n\2\([[:space:]].*\)/\1\3/
   ta
   P;D
' file

答案3

另一种sed方法,紧凑、便携且奇怪:

sed 'N;/^\(.*\)\( .*\)\(\n\1\)/!P;s//\3\2/;D'
  • 它使用该N;P;D方法在模式空间中始终有两行,因此它首先N附加下一行
  • /^\(.*\) .*\n\1/匹配以某个单词、空格、另一个单词、换行符和重复的初始单词开头的行,因此缓冲区中的两行共享它们的第一个 ( Locus) 单词。如果不是这种情况 ( !),P则打印第一行,因为它是完整的,我们稍后可以使用以下命令删除它D
  • 但如果该行匹配,那么我们有两行具有相同的第一个单词,并且可以进行替换,删除换行符和重复的单词。这就是为什么我在 的地址模式中添加了两个子组P,所以现在我不必重复它,而是通过使用空模式来重用它
  • 现在出现了技巧:我替换first second\nfirst\nfirst second,因此模式空间有一个空的第一行,后面是第二行,其中包含第一个单词和迄今为止我们拥有的所有第二个单词。现在,以下内容D将删除空的第一行并继续到目前为止收集的行。通过将 包含在匹配\n中,\3我们不需要\n替换,这将是不可移植的。

答案4

awk '!a[$1]{b[++p]=$1; a[$1]=$2;next} {a[$1]=sprintf("%s%s%s", a[$1], OFS, $2)} END {for (i=1; i<=p; i++) print b[i], a[b[i]]}' file

基于这篇文章如何根据第一个字段/元素对行进行分组

相关内容