我希望获取一个具有多行和多列的文件,水平添加(求和)具有匹配标题的列的值。在我的文件中(不是这个例子)我忽略了前 9 列和第一行,因为我希望它们不受影响地打印在结果中。
这些列不按重复顺序排列,并且列数比示例多得多。
这个想法是这样的:
输入:
var x y x y x y
a 1 0 1 1 0 1
b 1 1 0 0 1 1
c 1 1 0 0 0 0
输出:
var x y
a 2 2
b 2 2
c 1 1
这是我到目前为止所得到的......
awk -F '\t' '{FS==OFS} FNR==1; FNR>1 {for (i=10; i<=NF; i++) {} print}' FILE.tsv > FILE_norepcols.tsv
另外,如果可能的话,我试图了解如何更好地编写此代码,因此请建议我可以/应该更改的任何内容。
答案1
$ perl -lane '$,="\t";
print(qw/var x y/),next if $. == 1;
push @A, shift @F;
$A[$|--+1] += $_ for @F;
print splice @A;
' file
结果:
var x y
a 2 2
b 2 2
c 1 1
假设:
- 字段数量为奇数。
- Field#2 及之后的字段均为数字。
解释(简短的):
标题已明确打印,如 OP 所示。
@A
通过在使用该函数打印数组时清空数组,为每一行重新填充数组splice
。数组
@F
存储输入记录的字段$_
,零索引。数组的第一个元素(而不是第零个元素)
@A
从数组的前面移出@F
。请注意,每次读入新记录@F
时都会生成数组perl
。这类似于$1, $2, $3, ..., $NF
中的字段awk
。数组的其余部分
@A
是一个二进制(0|1)+1=>(1|2)
元素索引,它累加数组中的相应总和@F
。
希望它是清楚的。
答案2
使用相当冗长的变量名称和中间变量来帮助您理解发生了什么(而不是注释每一行或稍后添加解释):
$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==1 {
for (inFldNr=2; inFldNr<=NF; inFldNr++) {
fldName = $inFldNr
if ( !(fldName in fldName2outFldNr) ) {
outFldNr2name[++numOutFlds] = fldName
fldName2outFldNr[fldName] = numOutFlds
}
outFldNr = fldName2outFldNr[fldName]
out2inFldNrs[outFldNr,++numInFlds[outFldNr]] = inFldNr
}
printf "%s%s", $1, OFS
for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
outFldName = outFldNr2name[outFldNr]
printf "%s%s", outFldName, (outFldNr<numOutFlds ? OFS : ORS)
}
next
}
{
printf "%s%s", $1, OFS
for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
sum = 0
for (inFldIdx=1; inFldIdx<=numInFlds[outFldNr]; inFldIdx++) {
inFldNr = out2inFldNrs[outFldNr,inFldIdx]
sum += $inFldNr
}
printf "%s%s", sum, (outFldNr<numOutFlds ? OFS : ORS)
}
}
。
$ awk -f tst.awk file
var x y
a 2 2
b 2 2
c 1 1
答案3
使用 GNU datamash
:
<file datamash -W transpose |
datamash -W --headers -s -g1 sum 2-4 |
datamash --output-delimiter=' ' transpose
- 使用空格作为分隔符转置文件 (
-W
) - 对第一个字段 (
-g1
) 进行分组,在分组 (-s
) 之前进行排序,并对字段 2-4 上的值求和。第一个输入行被视为列标题并打印在输出中 (--headers
) - 转置结果并使用空格字符而不是制表符作为分隔符。
输出:
GroupBy(var) x y
sum(a) 2 2
sum(b) 2 2
sum(c) 1 1
使用以下命令打印结果sed
:
<file datamash -W transpose |
datamash -W --headers -s -g1 sum 2-4 |
datamash --output-delimiter=' ' transpose |
sed 's/^[^(]*(\([^)]*\))/\1/'
输出:
var x y
a 2 2
b 2 2
c 1 1
您可以使用以下cut
命令从输入文件中删除前九列并将它们添加回结果中paste
:
paste -d ' ' <(cut -d' ' -f-9 file) <(cut -d' ' -f10- file | datamash ... )
head -n1
可以使用 打印并跳过附加标题行tail -n+2
:
{
head -n1 file
paste -d ' ' <(tail -n+2 file | cut -d' ' -f-9) <(tail -n+2 file | cut -d' ' -f10- | datamash ... )
}