我有一个像这样的制表符分隔的 file1 (显示子集,实数矩阵 60x60000):
rowname header1 header2 header3 header4 header5 header6 header7 header8
rowname1 1 1 10 2 3 1 10 2
rowname2 0 7 200 3 37 1 2 1
还有另一个 file2 像这样:
header1,header2
header3
header4,header5
header6,header8
header7
我想对 file2 的每一行中指定的列进行求和:
rowname header1 header3 header4 header6 header7
rowname1 2 10 5 3 10
rowname2 7 200 40 2 2
所以,column1+column2,column3,column4+column5,column6+column8,column7……
有些列必须求和,有些则不需要,并且要求和的列并不总是连续的。
如果对列进行求和,则第一列的标题需要保留在输出文件中。
我想知道你是否有解决方案awk。到目前为止,我只知道如何存储标头条目:
awk '
NR==1 {
for (i=1; i<=NF; i++) {
f[$i] = i
}
答案1
awk '
FNR==NR{
newhdr[FNR]=$1 # new header name
newhdrcnt++ # number of new header names
for (i=1;i<=NF;i++)
hdrnames[FNR]=$0 # save new header names comma-separated
next
}
FNR==1{
# save column numbers for new header names in array hdrcols
for (i=1;i<=newhdrcnt;i++){
n=split(hdrnames[i], oldhdr, ",")
for(j=1;j<=n;j++){
for(k=2;k<=NF;k++){
if ($(k) == oldhdr[j]){
hdrcols[i]=(j==1 ? "" : hdrcols[i] ",") k;
if (j==n) break
}
}
}
}
# print header
printf $1
for (i=1;i<=newhdrcnt;i++)
printf FS newhdr[i]
printf ORS
next
}
{ # print data
printf $1
for (i=1;i<=newhdrcnt;i++){
n=split(hdrcols[i], cols, ",")
res=0
for(j=1;j<=n;j++)
res=res+$(cols[j])
printf FS res
}
printf ORS
}
' FS="," file2 FS="\t" file1
输出:
rowname header1 header3 header4 header6 header7
rowname1 2 10 5 3 10
rowname2 7 200 40 2 2
答案2
我想我伤害了我的大脑,但是使用gawk
#!/usr/bin/awk -f
BEGIN{FS="\t"}
NR==FNR{H[NR]=","$0","; next}
FNR==1{for (i=2;i<=NF; i++) T[i]=","$i","; printf $1;
for (i=1; i<=length(H); i++){split(H[i],sp,","); printf "\t"sp[2]}; print ""}
FNR>1{delete S; for (i=2; i<=NF; i++)
{for (h in H) {if (H[h] ~ T[i]) S[h]+=$i}} printf $1;
for (i=1; i<=length(H); i++) printf "\t"S[i]; print""}
调用为
./script file2 file1 | column -t
输出
rowname header1 header3 header4 header6 header7
rowname1 2 10 5 3 10
rowname2 7 200 40 2 2
根据 @paul_pedant 对 @freddy 的评论,未指定的列被删除
演练
设置FS
BEGIN{FS="\t"}
加载要求和到数组中的列列表,H[]
在每一端添加“,”(以避免后面的正则表达式不匹配)
NR==FNR{H[NR]=","$0","; next}
如果它是数据文件的第一行,则加载数组中的列标题,T[]
并在标题名称的每一端添加“,”$i
FNR==1{for (i=2;i<=NF; i++) T[i]=","$i",";
...并打印提取的标题作为总和指定字符串的第一部分
printf $1; for (i=1; i<=length(H); i++){split(H[i],sp,","); printf "\t"sp[2]}; print ""}
对于每个数据行,清除求和数组S[]
,然后遍历字段
FNR>1{delete S; for (i=2; i<=NF; i++)
检查每个H[]
字段名称T[]
,如果存在,则将字段值添加到索引到匹配项的总和数组中S[h]
{for (h in H) {if (H[h] ~ T[i]) S[h]+=$i}}
在每行的末尾打印
printf $1; for (i=1; i<=length(H); i++) printf "\t"S[i]; print""}