2011-11-28 93 views
10

我想计算所有行中每行的值的百分比,并将其添加为另一列。 输入(定界符是\吨):如何添加具有百分比的列

1 10  
2 10 
3 20 
4 40 

与表示基于第二列中的值计算出的百分比加入第三列所需的输出:

1 10 12.50 
2 10 12.50 
3 20 25.00 
4 40 50.00 

我试图自己做,但是当我所有线路的计算总数我不知道如何保持线路的其余部分不变。非常感谢您的帮助!

回答

12

在这里你去,一个 一步awk的解决方案 -

awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file

[jaypal:~/Temp] cat file 
1 10  
2 10 
3 20 
4 40 
[jaypal:~/Temp] awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file 
1 10 12.5 
2 10 12.5 
3 20 25 
4 40 50 

更新:如果所需的选项卡中输出,那么刚才设置的OFS变量设置为 “\ t” 的。

[jaypal:~/Temp] awk -v OFS="\t" 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file 
1 10 12.5 
2 10 12.5 
3 20 25 
4 40 50 

突围图案{动作}语句:

  • 第一图案是NR==FNR。 FNR是awk的内置变量,用于跟踪给定文件中的记录数(默认情况下由新行分隔)。因此,在我们的情况下,FNR将为4.NR与FNR类似,但不会重置为0.它将继续增长。所以在我们的例子中,NR就是8.

  • 这种模式只对前4条记录是真实的,这就是我们想要的。仔细阅读4条记录后,我们将总数分配给变量a。请注意,我们没有初始化它。在awk我们不必。然而,如果整个第2列为0,那么这将会中断。因此,您可以通过在第二个操作语句中放置一个if语句来处理它,即,只有当> 0或其他部分除以0时才进行分割。

  • next是需要的,因为我们并不想要第二个模式{action}语句来执行。 next告诉awk停止进一步操作并移至下一条记录。

  • 一旦四条记录被解析,下一个模式{动作}开始,这是非常简单的。执行百分比并将列1和列2与其旁边的百分比一起打印。

注:正如在评论中提及@lhf,这一个班轮只会只要你拥有的数据文件中的设置工作。如果您通过管道传递数据,它将不起作用。

在评论,有事情的方式,使从pipe而不是fileawk one-liner取输入的讨论。那么我能想到的唯一方法是将列值存储在array中,然后使用for loop将每个值与其百分比一起吐出。

现在arraysawkassociative,并且从不为了,即拉出阵列的值不会以相同的顺序,因为他们走了进去。因此,如果这是确定的,然后下面的一行应该工作。

[jaypal:~/Temp] cat file 
1 10  
2 10 
3 20 
4 40 

[jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}' 
2 10 12.5 
3 20 25 
4 40 50 
1 10 12.5 

要让它们按顺序排列,可以将结果传递给sort

[jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}' | sort -n 
1 10 12.5 
2 10 12.5 
3 20 25 
4 40 50 
+0

就是这样。谢谢! – Martin

+0

没问题。 :)我会提供一些解释供参考。 –

+1

不错,但不是真的一次过。特别是,它不能用作过滤器,即从标准输入读取。 – lhf

1

您需要将其转义为%%。例如:

printf("%s\t%s\t%s%%\n", $1, $2, $3) 
+0

谢谢,对不起,如果没有在问题中解释正确 - 我没有%符号的问题(我不需要它),我的问题是如何计算值本身。 – Martin

+0

哦......对!对不起,错过了这个问题! – jsalonen

2

你可以做一对夫妇经过

#!/bin/bash 

total=$(awk '{total=total+$2}END{print total}' file) 
awk -v total=$total '{ printf ("%s\t%s\t%.2f\n", $1, $2, ($2/total)*100)}' file 
+0

谢谢。这也适用,但是我发现Jaypal建议的解决方案更易于使用,所以我选择了他的解决方案作为答案。 – Martin

0

也许有更好的方式,但我会通过文件两次。

BEGIN { 
     ## Tab as field separator. 
     FS = "\t"; 
} 

## First pass of input file. Get total from second field. 
ARGIND == 1 { 
     total += $2; 
     next; 
} 

## Second pass of input file. Print each original line and percentage as third field. 
{ 
     printf("%s\t%2.2f\n", $0, $2 * 100/total); 
} 

运行脚本在我的Linux机器:

gawk -f script.awk infile infile 

,并导致 'script.awk' 的

1  10 
2  10 
3  20 
4  40 

内容: 'INFILE' 的

内容:

1  10  12.50 
2  10  12.50 
3  20  25.00 
4  40  50.00 
+0

谢谢。这也适用。 – Martin