我有一个具有以下结构的文件:
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
$ 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
基于这篇文章如何根据第一个字段/元素对行进行分组