我的数据位于多行中,我想将其转置为制表符分隔的多列,即
ABC 0.98 0.58 5.87 0.01
DEF 0.88 5.85 6.89 0.25
GHI 8.99 5.66 4.78 6.22
进入
ABC DEF GHI
0.98 0.88 8.99
0.58 5.85 5.66
5.87 6.89 4.78
0.01 0.25 6.22
您能帮我解决这个问题,以便我可以获得上述格式的输出吗?
答案1
使用 GNU datamash
:
$ datamash -W transpose <file
ABC DEF GHI
0.98 0.88 8.99
0.58 5.85 5.66
5.87 6.89 4.78
0.01 0.25 6.22
这用于datamash
将空格分隔的行转置为制表符分隔的列。
您可以使用该选项设置另一个输出分隔符--output-delimiter
。这里我使用了一个空格,它似乎重新创建了您的预期输出:
$ datamash -W --output-delimiter=' ' transpose <file
ABC DEF GHI
0.98 0.88 8.99
0.58 5.85 5.66
5.87 6.89 4.78
0.01 0.25 6.22
答案2
在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ cat tst.awk
{
for ( rowNr=1; rowNr<=NF; rowNr++ ) {
vals[rowNr,NR] = $rowNr
}
}
END {
for ( rowNr=1; rowNr<=NF; rowNr++ ) {
for ( colNr=1; colNr<=NR; colNr++ ) {
printf "%s%s", vals[rowNr,colNr], (colNr<NR ? OFS : ORS)
}
}
}
$ awk -f tst.awk file
ABC DEF GHI
0.98 0.88 8.99
0.58 5.85 5.66
5.87 6.89 4.78
0.01 0.25 6.22
您可以使用以下命令更简短地执行上述操作:
$ cat tst.awk
{
for ( rowNr=1; rowNr<=NF; rowNr++ ) {
vals[rowNr] = (rowNr in vals ? vals[rowNr] OFS : "") $rowNr
}
}
END {
for ( rowNr=1; rowNr<=NF; rowNr++ ) {
print vals[rowNr]
}
}
$ awk -f tst.awk file
ABC DEF GHI
0.98 0.88 8.99
0.58 5.85 5.66
5.87 6.89 4.78
0.01 0.25 6.22
但请注意,在这种情况下,您不断地为同一个变量重新分配新值,vals[rowNr]
与为每个新变量分配一次相比,这在 awk 中是一个相对较慢的操作,因为 awk 必须确定需要存储多少内存新值,将旧值移动到新的内存位置,追加新值,然后释放旧位置,最终需要一些大内存块而不是许多小内存块,因此 awk 更难找到可用的内存内存随着变量大小的增加而增加。它还将输出的格式化与输入的读取混合在一起,因此生成的代码比第一个脚本耦合得更紧密。不过这并不可怕,至少代码很简洁。
答案3
听起来像是 BSD 的工作rs
(自 1983 年起就包含在 BSD 中,但在其他系统上默认安装的情况并不常见):
$ cat file
ABC 0.98 0.58 5.87 0.01
DEF 0.88 5.85 6.89 0.25
GHI 8.99 5.66 4.78 6.22
$ rs -T < file
ABC DEF GHI
0.98 0.88 8.99
0.58 5.85 5.66
5.87 6.89 4.78
0.01 0.25 6.22
答案4
使用乐(以前称为 Perl_6)
raku -e '.put for [Z] lines.map(*.words);'
或(两步):
raku -e 'my @a = lines.map(*.words); .put for [Z] @a;'
输入示例:
ABC 0.98 0.58 5.87 0.01
DEF 0.88 5.85 6.89 0.25
GHI 8.99 5.66 4.78 6.22
示例输出:
ABC DEF GHI
0.98 0.88 8.99
0.58 5.85 5.66
5.87 6.89 4.78
0.01 0.25 6.22
简要解释第二个例子,lines
被读入@a
数组,并且每个都被分成空白界定的words
(即列)。在第二个语句中, data 是 out put
,但是[Z]
使用类似 zip 的归约运算符来获取第一个数组元素的第一个字,并与第二个数组元素的第一个字和第三个数组的第一个字一起返回元素等
或者,如果您的 CSV/TSV 要求更严格,您可以Text::CSV
在命令行使用 Raku 的模块:
~$ raku -MText::CSV -e '.put for [Z] csv(in => $*IN, sep => " ");' < file
ABC DEF GHI
0.98 0.88 8.99
0.58 5.85 5.66
5.87 6.89 4.78
0.01 0.25 6.22
https://docs.raku.org/language/operators#index-entry-[]_(reduction_metaoperators)
https://unix.stackexchange.com/a/670344/227738
https://raku.org