awk运行总计数和总和

时间:2015-04-10 17:16:42

标签: awk

想知道如何每天计算供应商80%-20%的规则贡献。

Input.csv

Date,Region,Vendor,Amount
5-Apr-15,east,cc,50
5-Apr-15,east,dd,15
5-Apr-15,south,bb,15
5-Apr-15,south,aa,10
7-Apr-15,east,cc,123
7-Apr-15,south,bb,88
7-Apr-15,south,aa,40
7-Apr-15,west,ss,30
7-Apr-15,west,rr,20

在上面的输入中,基于$ 1字段需要填充运行总额金额,然后计算当天金额的运行总和的百分比

Date,Region,Vendor,Amount,RunningSum,%RunningSum
5-Apr-15,east,cc,50,50,56%   (RunningSum=50 , %RunningSum=50/90(Total Amount for the day) 
5-Apr-15,east,dd,15,65,72%   (RunningSum=50+15, %RunningSum=65/90)
5-Apr-15,south,bb,15,80,89%  (RunningSum=65+15, %RunningSum=80/90)
5-Apr-15,south,aa,10,90,100% (RunningSum=80+10, %RunningSum=90/90)

一旦得出80%或首次击中80%以上需要考虑为80%贡献剩余的项目需要考虑为20%的贡献。

预期产出:

Date,Countof80%Vendor, SumOf80%Vendor, Countof20%Vendor, SumOf20%Vendor
5-Apr-15,3,80,1,10
7-Apr-15,3,251,2,50

任何建议......

3 个答案:

答案 0 :(得分:3)

$ cat tst.awk
BEGIN { FS=OFS="," }
NR==FNR { tot[$1] += $NF; next }
FNR==1 { print $0, "RunningSum", "%RunningSum"; next }
{ sum[$1]+=$NF; print $0, sum[$1], int(0.5+sum[$1]*100/tot[$1])"%" }

$ awk -f tst.awk file file
Date,Region,Vendor,Amount,RunningSum,%RunningSum
5-Apr-15,east,cc,50,50,56%
5-Apr-15,east,dd,15,65,72%
5-Apr-15,south,bb,15,80,89%
5-Apr-15,south,aa,10,90,100%
7-Apr-15,east,cc,123,123,41%
7-Apr-15,south,bb,88,211,70%
7-Apr-15,south,aa,40,251,83%
7-Apr-15,west,ss,30,281,93%
7-Apr-15,west,rr,20,301,100%

我在问题的第一部分提供了上面的答案,但我不知道从Once it is derived 80%开始的部分是如何与它相关的。在给定相同输入的情况下,您想要输出到另一个文件的其他内容吗?附加到上面的输出?一个不同的脚本?别的什么?

答案 1 :(得分:2)

这在awk中相对比较复杂,但现在这样做了。

awk -F , '{vals[$1,++nums[$1]]=$4}END{
    for(d in nums){
        tot=0
        for(i=1;i<=nums[d];i++)tot+=vals[d,i]
        n=0;s=0
        for(i=1;i<=nums[d];i++){
            n++;s+=vals[d,i]
            if(s>=tot*0.8){s80=s;n80=n;s=0;n=0}
        }
        printf("%s,%d,%d,%d,%d\n",d,n80,s80,n,s)
    }
}' vendors.txt

这会为每个日期创建独立的值列表,并在收集所有数据循环后通过这些列表并计算出80%点的位置。

这假设该文件仅包含数据(没有“Date,Region,Vender ..”标题)。如果你想要一个单行版本用于复制和粘贴,那么它是:

awk -F , '{vals[$1,++nums[$1]]=$4}END{for(d in nums){tot=0;for(i=1;i<=nums[d];i++)tot+=vals[d,i];n=0;s=0;for(i=1;i<=nums[d];i++){n++;s+=vals[d,i];if(s>=tot*0.8){s80=s;n80=n;s=0;n=0}};printf("%s,%d,%d,%d,%d\n",d,n80,s80,n,s)}}' vendors.txt

编辑:如果你想为每个地区分别计算,你只需要将$ 1部分改为$ 1“,”$ 2,以连接日期和地区:

awk -F , '{a=$1","$2;vals[a,++nums[a]]=$4}END{for(d in nums){tot=0;for(i=1;i<=nums[d];i++)tot+=vals[d,i];n=0;s=0;for(i=1;i<=nums[d];i++){n++;s+=vals[d,i];if(s>=tot*0.8){s80=s;n80=n;s=0;n=0}};printf("%s,%d,%d,%d,%d\n",d,n80,s80,n,s)}}' vendors.txt

说实话,不过,我有点不清楚你究竟要用这个衡量什么。例如,构成销售额80%的供应商数量并不是唯一定义的 - 这取决于您对它们进行排序的顺序。如果您先对所有小额销售进行排序,那么您获得的数字将大于排序时的数量。大销量第一。如果您不按任何特定顺序对它们进行排序,您将得到一些中间的东西。如果您想要的是有多少最大的供应商占销售额的80%,那么您需要在计算之前对数组进行排序。

答案 2 :(得分:2)

在日期发生变化时,这是一个awk脚本:

#!/usr/bin/awk -f

BEGIN {
    FS=OFS=","
    print "Date" OFS "Countof80%Vendor" OFS "SumOf80%Vendor" OFS "Countof20%Vendor" OFS "SumOf20%Vendor"
}

FNR==1 { next } # skip the header

last_1 != $1 && FNR > 2 {
    output( a, cnt, tot, last_1 )
    cnt = tot = 0
    delete( a )
}

{ tot += $4; a[++cnt] = tot; last_1 = $1 }

END { output( a, cnt, tot, last_1 ) }


func output( a, cnt, tot, last_date,                 perc80, i, runsum ) {
    perc80 = .8 * tot
    for(i=1; i<=cnt; i++) {
        runsum = a[i]
        if(runsum >= perc80) {
            print last_date OFS i OFS runsum OFS (cnt-i) OFS (tot-runsum)
            break
        }
    }
}

以下是细分:

  • BEGIN - 将FSOFS设置为,,然后打印标题
  • FNR==1 - 跳过标题行
  • last_1 != $1 && FNR > 2 - 只要$1字段更改值,就可以通过调用函数output打印出运行总计。然后,清除cnttot变量并删除数组a
  • 对于每个非标题行,$4中的tot总和。然后设置a[++cnt] = tot以捕获到目前为止读取的元素数量的索引处的运行总计。最后,在完成所有其他处理后,将last_1变量重置为$1
  • END - 捕获最终日期分组并使用output
  • 打印

关于函数output

  • perc80 - 计算tot值的80%标记。
  • 使用ai行走cnt数组元素。
  • a[i]的值存入runsum以明确/减少a[i]来电
  • if(runsum >= perc80)打印输出行然后中断。

运行此脚本会提供以下输出:

Date,Countof80%Vendor,SumOf80%Vendor,Countof20%Vendor,SumOf20%Vendor
5-Apr-15,3,80,1,10
7-Apr-15,3,251,2,50