如何折叠具有相同名称的 KO 类别并打印分配给数组中每个类别的基因名称,如下例所示。
我有这个:
K00002 gene_65472
K00002 gene_212051
K00002 gene_403626
K00003 gene_666
K00003 gene_5168
K00003 gene_7635
K00003 gene_12687
K00003 gene_175295
K00003 gene_647659
K00003 gene_663019
K00004 gene_88381
K00005 gene_30485
K00005 gene_193699
K00005 gene_256294
K00005 gene_307497
并想要这个:
K00002 gene_65472 gene_212051 gene_403626
K00003 gene_666 gene_5168 gene_7635 gene_12687 gene_175295 gene_647659 gene_663019
K00004 gene_88381
K00005 gene_30485 gene_193699 gene_256294 gene_307497
以下命令有效(取自罗艾玛的回答):
tr -d '\r' < file| awk '$1 != p { if (p>"") {printf "\n"} printf "%s",$1; p=$1 } { printf "\t%s",$2 } END { if(p>"") {printf "\n"} }' > output
答案1
更多相同的
awk '$1 != p { if (p>"") {printf "\n"} printf "%s",$1; p=$1 } { printf "\t%s",$2 } END { if(p>"") {printf "\n"} }' datafile
K00002 gene_65472 gene_212051 gene_403626
K00003 gene_666 gene_5168 gene_7635 gene_12687 gene_175295 gene_647659 gene_663019
K00004 gene_88381
K00005 gene_30485 gene_193699 gene_256294 gene_307497
如果你不想分开标签然后将 更改\t
为空格。
它的工作原理如下:
# Each line is processed in turn. "p" is the previous line's key field value
# Key field isn't the same as before
$1 != p {
# Flush this line if we have printed something already
if (p > "") { printf "\n" }
# Print the key field name and set it as the current key field
printf "%s", $1; p = $1
}
# Every line, print the second value on the line
{ printf "\t%s", $2 }
# No more input. Flush the line if we have already printed something
END {
if (p > "") { printf "\n" }
}
来自模糊的 评论你是制作针对每个人的答案,似乎根本问题是您正在使用在 Windows 系统上生成的数据文件并期望它在 UNIX/Linux 平台上工作。不要那样做。或者,如果必须,请先将文件转换为正确的格式。
dos2unix < datafile | awk '...' # As above
tr -d '\r' < data file | awk '...' # Also as above
答案2
文件:
K00002 gene_65472
K00002 gene_212051
K00002 gene_403626
K00003 gene_666
K00003 gene_5168
K00003 gene_7635
K00003 gene_12687
K00003 gene_654221
K00003 gene_663019
K00004 gene_88381
K00005 gene_30485
K00005 gene_193699
K00005 gene_256294
使用 awk:
awk '1 {if (a[$1]) {a[$1] = a[$1]" "$2} else {a[$1] = $2}} END {for (i in a) { print i,a[i]}}' file
输出:
K00002 gene_65472 gene_212051 gene_403626
K00003 gene_666 gene_5168 gene_7635 gene_12687 gene_654221 gene_663019
K00004 gene_88381
K00005 gene_30485 gene_193699 gene_256294
我拿了这个邮政作为参考。
答案3
使用米勒http://johnkerl.org/miller/doc
和
mlr --csv --implicit-csv-header --headerless-csv-output cat -n -g 1 then label a,b,c then reshape -s a,c then unsparsify --fill-with "" input.csv
以及此示例 csv 输入
A,234
A,4945
B,8798
B,8798
B,790
你将会拥有
A,234,4945,
B,8798,8798,790
答案4
假设您的值不包含空格并且以空格分隔;还假设您的数据位于名为的文件中file
(请参阅下面的制表符分隔版本):
for x in $(<file cut -d ' ' -f 1 | sort | uniq); do
printf '%s %s\n' "$x" "$(grep "$x" file | cut -d ' ' -f 2- | tr '\n' ' ' | sed 's/.$//')"
done
这会:
- 提取第一个字段的不同值:
cut
-f 1
仅选择一行的第一个块 ( ),并在每个空格 (-d ' '
) 处将其断开;sort | uniq
将对第一个字段的值进行排序并仅输出每个字段一次(或者,更短、更高效:)sort -u
;
- 对于每个:
file
从with中提取所有相关行grep
;cut
使用(-f 2-
表示“获取第二个及后续字段”)去除第一个字段;- 将余数转换为空格分隔值列表 (
tr
); - 去掉最后一个字符——一个不需要的空格——使用
sed
(是的,这真的很不优雅); - 将结果连接到第一个字段的值并打印到标准输出。
如果您的输入是制表符分隔的并且您想要制表符分隔的输出,则上面的代码将变为:
for x in $(<file cut -f 1 | sort | uniq); do
printf '%s\t%s\n' "$x" "$(grep "$x" file | cut -f 2- | tr '\n' '\t' | sed 's/.$//')"
done
笔记:
- 性能:这种方法的执行时间明显高于
awk
基于解决方案的执行时间(我测试过罗艾玛的回答)。至少是一个数量级。 - 另一方面,即使输入文件未排序,此方法也有效。
- 尽管这种解决方案是有效完成工作的快速(而且肮脏?)方法,但通常不建议使用 shell 循环处理文本;参见参考“为什么使用 shell 循环处理文本被认为是不好的做法?”。