我有一个制表符分隔的 ( \t
) 文件,其中包含两列:
a_1 T_b
a_2 T_c
a_2 T_d
a_2 T_e
a_3 T_f
a_4 B_a
a_4 B_b
我现在想将其转换为每行一个样本的文件,每个样本的所有值作为同一行上以空格分隔的第三个字段,如下所示:
a_1 T_b
a_2 T_c T_d T_e
a_3 T_f
a_4 B_a B_b
我刚刚掌握了awk从第二个文件到第一个文件的转换。那么你能给我一些建议吗,请原谅我糟糕的unix技能。多谢。
答案1
使用 GNUdatamash
按第一个制表符分隔的列进行分组并折叠另一列:
$ datamash groupby 1 collapse 2 <file
a_1 T_b
a_2 T_c,T_d,T_e
a_3 T_f
a_4 B_a,B_b
这假设文件按第一个字段排序。如果不是,则使用datamash
其-s
( --sort
) 选项,或将数据通过管道传输到datamash
viasort
。
如果您希望第二个字段的“子字段”由空格而不是逗号分隔,请使用以下命令将逗号替换为空格awk
:
$ datamash groupby 1 collapse 2 <file | awk -F '\t' 'BEGIN { OFS=FS } { gsub(","," ",$2) }; 1'
a_1 T_b
a_2 T_c T_d T_e
a_3 T_f
a_4 B_a B_b
答案2
这是一种方法:
$ awk -F'\t' '
{
$1 in a? a[$1]=a[$1]" "$2 : a[$1]=$2
}
END{
for(i in a){print i"\t"a[i]}
}' file
a_1 T_b
a_2 T_c T_d T_e
a_3 T_f
a_4 B_a B_b
这里的想法是,我们创建一个关联数组,a
其键是第一个字段,即样本名称。然后,对于每一行,如果该样本已作为键存在,我们将其值设置为该行的第二个字段,如果不是,我们将当前的第二个字段附加到现有值。同样的事情可以写得更详细,如下所示:
$ awk -F'\t' '
{
if($1 in a){ a[$1]=a[$1]" "$2 }
else { a[$1]=$2 }
}
END{
for(i in a){print i"\t"a[i]}
}' file
答案3
使用 Raku(以前称为 Perl_6)
raku -e 'my %hash; for lines>>.words() {%hash.append( .[0] => .[1] )}; .say for %hash.sort;'
或者
raku -e 'my %hash; for lines.map(*.words) -> ($k,$v) {%hash.append: $k => $v }; .say for %hash.sort;'
简而言之,Raku 中内置的哈希功能用于解决这个问题。行被读取并分成 (空格分隔) words
。单词被附加到 hash 中%hash
,第一列成为键,第二列成为值。 Raku 的=>
“胖箭头”运算符用于构造键值对,并且append
例程根据键累加值(哈希不能有重复的键)。
输入示例:
a_1 T_b
a_2 T_c
a_2 T_d
a_2 T_e
a_3 T_f
a_4 B_a
a_4 B_b
示例输出:
a_1 => T_b
a_2 => [T_c T_d T_e]
a_3 => T_f
a_4 => [B_a B_b]
.say
将上面的代码更改为.put
获取\t
制表符分隔的输出。或者定义您自己的字段分隔符:下面的示例中,第一列与第二列用 4 个空格分隔,value
第二列用逗号分隔(如果需要,请更改为空格):
~$ raku -e 'my %hash; for lines.map(*.words) -> ($k,$v) {%hash.append: $k => $v }; for %hash.sort {put .key, " ", .value.join(",")};'
a_1 T_b
a_2 T_c,T_d,T_e
a_3 T_f
a_4 B_a,B_b
https://docs.raku.org/language/hashmap#Mutable_hashes_and_immutable_maps
https://docs.raku.org/routine/=%3E
https://raku.org