shell 中多个文件的平均值
Average of multiple files in shell
我想计算 15 个文件的平均值:- ifile1.txt、ifile2.txt、.....、ifile15.txt。每个文件的列数和行数相同。部分数据看起来像
ifile1.txt ifile2.txt ifile3.txt
3 5 2 2 . 1 2 1 3 . 4 3 4 1 .
1 4 2 1 . 1 3 0 2 . 5 3 1 5 .
4 6 5 2 . 2 5 5 1 . 3 4 3 1 .
5 5 7 1 . 0 0 1 1 . 4 3 4 0 .
. . . . . . . . . . . . . . .
我想找到一个新文件,它将显示这 15 个文件的平均值。
ofile.txt
2.66 3.33 2.33 2 . (i.e. average of 3 1 4, average of 5 2 3 and so on)
2.33 3.33 1 2.66 .
3 5 4.33 1.33 .
3 2.33 4 0.66 .
. . . . .
我正在尝试跟随,但出现错误
awk'{for (i=1; i<=NF; i++)} rows=FNR;cols=NF} END
{for (i=1; i<=rows; i++){for (j=1; j<=cols; j++)
s+=$i;print [=13=],s/NF;s=0}}' ifile* > ofile.txt
您在读取原始文件时需要将字段的总和保存到一个数组中。您无法访问 END
块中的 [=11=]
和 i
,因为那时没有输入行。
awk '{rows=FNR; cols=NF; for (i = 1; i <= NF; i++) { total[FNR, i] += $i }}
FILENAME != lastfn { count++; lastfn = FILENAME }
END { for (i = 1; i <= rows; i++) {
for (j = 1; j <= cols; j++) {
printf("%s ", total[i, j]/count)
}
printf("\n")
}
}' ifile* > ofile.txt
写成:
awk'{for (i=1; i<=NF; i++)} rows=FNR;cols=NF} END
…
你得到 'command not found' 作为错误,因为你必须在 awk
和引号内的脚本之间留下 space。当你修复它时,你开始遇到问题,因为在脚本的第一行有两个 }
而只有一个 {
。
当您着手解决问题时,您将需要一个二维数组,按行号和列号索引,对文件中的值求和。您还需要知道处理的文件数和列数。然后,您可以安排在 END 块中迭代二维数组。
awk 'FNR == 1 { nfiles++; ncols = NF }
{ for (i = 1; i < NF; i++) sum[FNR,i] += $i
if (FNR > maxnr) maxnr = FNR
}
END {
for (line = 1; line <= maxnr; line++)
{
for (col = 1; col < ncols; col++)
printf " %f", sum[line,col]/nfiles;
printf "\n"
}
}' ifile*.txt
给定问题中的三个数据文件:
ifile1.txt
3 5 2 2
1 4 2 1
4 6 5 2
5 5 7 1
ifile2.txt
1 2 1 3
1 3 0 2
2 5 5 1
0 0 1 1
ifile3.txt
4 3 4 1
5 3 1 5
3 4 3 1
4 3 4 0
我展示的脚本产生了:
2.666667 3.333333 2.333333
2.333333 3.333333 1.000000
3.000000 5.000000 4.333333
3.000000 2.666667 4.000000
如果要控制小数位数为2,那么用%.2f
代替%f
。
$ { head -n1 ifile1.txt; paste ifile*.txt;} | awk 'NR==1{d=NF; next;} {for (i=1;i<=d;i++) {s=0; for (j=i;j<=NF;j+=d) s+=$j; printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";}}'
2.67 3.33 2.33 2.00
2.33 3.33 1.00 2.67
3.00 5.00 4.33 1.33
3.00 2.67 4.00 0.67
此脚本计算每一行并在移动到下一行之前打印结果。因此,脚本不需要一次将所有数据保存在内存中。如果数据文件很大,这很重要。
工作原理
{ head -n1 ifile1.txt; paste ifile*.txt;}
这只打印 ifile1.txt
的第一行。然后,paste
命令使它打印所有合并文件的第一行,然后是合并的第二行,依此类推:
$ paste ifile*.txt
3 5 2 2 1 2 1 3 4 3 4 1
1 4 2 1 1 3 0 2 5 3 1 5
4 6 5 2 2 5 5 1 3 4 3 1
5 5 7 1 0 0 1 1 4 3 4 0
|
管道符号导致上述命令的输出作为输入发送到 awk。依次处理每个 awk 命令:
NR==1{d=NF; next;}
对于第一行,我们将列数保存在变量 d
中。然后,我们跳过其余的命令并从输入的 next
行重新开始。
for (i=1;i<=d;i++) {s=0; for (j=i;j<=NF;j+=d) s+=$j; printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";}
这会将各个文件的数字相加并打印平均值。
作为多行脚本:
{
head -n1 ifile1.txt
paste ifile*.txt
} |
awk '
NR==1 {d=NF; next;}
{
for (i=1;i<=d;i++)
{
s=0; for (j=i;j<=NF;j+=d)
s+=$j;
printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";
}
}
我想计算 15 个文件的平均值:- ifile1.txt、ifile2.txt、.....、ifile15.txt。每个文件的列数和行数相同。部分数据看起来像
ifile1.txt ifile2.txt ifile3.txt
3 5 2 2 . 1 2 1 3 . 4 3 4 1 .
1 4 2 1 . 1 3 0 2 . 5 3 1 5 .
4 6 5 2 . 2 5 5 1 . 3 4 3 1 .
5 5 7 1 . 0 0 1 1 . 4 3 4 0 .
. . . . . . . . . . . . . . .
我想找到一个新文件,它将显示这 15 个文件的平均值。
ofile.txt
2.66 3.33 2.33 2 . (i.e. average of 3 1 4, average of 5 2 3 and so on)
2.33 3.33 1 2.66 .
3 5 4.33 1.33 .
3 2.33 4 0.66 .
. . . . .
我正在尝试跟随,但出现错误
awk'{for (i=1; i<=NF; i++)} rows=FNR;cols=NF} END
{for (i=1; i<=rows; i++){for (j=1; j<=cols; j++)
s+=$i;print [=13=],s/NF;s=0}}' ifile* > ofile.txt
您在读取原始文件时需要将字段的总和保存到一个数组中。您无法访问 END
块中的 [=11=]
和 i
,因为那时没有输入行。
awk '{rows=FNR; cols=NF; for (i = 1; i <= NF; i++) { total[FNR, i] += $i }}
FILENAME != lastfn { count++; lastfn = FILENAME }
END { for (i = 1; i <= rows; i++) {
for (j = 1; j <= cols; j++) {
printf("%s ", total[i, j]/count)
}
printf("\n")
}
}' ifile* > ofile.txt
写成:
awk'{for (i=1; i<=NF; i++)} rows=FNR;cols=NF} END
…
你得到 'command not found' 作为错误,因为你必须在 awk
和引号内的脚本之间留下 space。当你修复它时,你开始遇到问题,因为在脚本的第一行有两个 }
而只有一个 {
。
当您着手解决问题时,您将需要一个二维数组,按行号和列号索引,对文件中的值求和。您还需要知道处理的文件数和列数。然后,您可以安排在 END 块中迭代二维数组。
awk 'FNR == 1 { nfiles++; ncols = NF }
{ for (i = 1; i < NF; i++) sum[FNR,i] += $i
if (FNR > maxnr) maxnr = FNR
}
END {
for (line = 1; line <= maxnr; line++)
{
for (col = 1; col < ncols; col++)
printf " %f", sum[line,col]/nfiles;
printf "\n"
}
}' ifile*.txt
给定问题中的三个数据文件:
ifile1.txt
3 5 2 2
1 4 2 1
4 6 5 2
5 5 7 1
ifile2.txt
1 2 1 3
1 3 0 2
2 5 5 1
0 0 1 1
ifile3.txt
4 3 4 1
5 3 1 5
3 4 3 1
4 3 4 0
我展示的脚本产生了:
2.666667 3.333333 2.333333
2.333333 3.333333 1.000000
3.000000 5.000000 4.333333
3.000000 2.666667 4.000000
如果要控制小数位数为2,那么用%.2f
代替%f
。
$ { head -n1 ifile1.txt; paste ifile*.txt;} | awk 'NR==1{d=NF; next;} {for (i=1;i<=d;i++) {s=0; for (j=i;j<=NF;j+=d) s+=$j; printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";}}'
2.67 3.33 2.33 2.00
2.33 3.33 1.00 2.67
3.00 5.00 4.33 1.33
3.00 2.67 4.00 0.67
此脚本计算每一行并在移动到下一行之前打印结果。因此,脚本不需要一次将所有数据保存在内存中。如果数据文件很大,这很重要。
工作原理
{ head -n1 ifile1.txt; paste ifile*.txt;}
这只打印
ifile1.txt
的第一行。然后,paste
命令使它打印所有合并文件的第一行,然后是合并的第二行,依此类推:$ paste ifile*.txt 3 5 2 2 1 2 1 3 4 3 4 1 1 4 2 1 1 3 0 2 5 3 1 5 4 6 5 2 2 5 5 1 3 4 3 1 5 5 7 1 0 0 1 1 4 3 4 0
|
管道符号导致上述命令的输出作为输入发送到 awk。依次处理每个 awk 命令:
NR==1{d=NF; next;}
对于第一行,我们将列数保存在变量
d
中。然后,我们跳过其余的命令并从输入的next
行重新开始。for (i=1;i<=d;i++) {s=0; for (j=i;j<=NF;j+=d) s+=$j; printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";}
这会将各个文件的数字相加并打印平均值。
作为多行脚本:
{
head -n1 ifile1.txt
paste ifile*.txt
} |
awk '
NR==1 {d=NF; next;}
{
for (i=1;i<=d;i++)
{
s=0; for (j=i;j<=NF;j+=d)
s+=$j;
printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";
}
}