Unix,对 file.csv 中的行进行分组并对列求和

Unix, group rows and sum values of columns from file.csv

我有这个file.csv

"201707"|"51976551"|1|0|1|"20170702"
"201707"|"51955194"|1|0|0|"20170702"
"201707"|"51923555"|1|0|1|"20170702"
"201707"|"51976551"|1|0|1|"20170703"
"201707"|"51955194"|1|0|0|"20170703"
"201707"|"51923555"|1|0|1|"20170703"
"201707"|"51960597"|1|0|0|"20170703"

我希望结果是按数字分组并对第 3、4 和 5 列求和

"201707"|"51976551"|2|0|2
"201707"|"51955194"|2|0|0
"201707"|"51923555"|2|0|2
"201707"|"51960597"|1|0|0

我试过:

cat file.csv | awk -F"|" '
  { a[] +=  }
  END {
    for (i in a) {
      printf "%s|%s\n", i, a[i];
    }
  }
'

结果是:

"51976551"|2
"51955194"|2
"51923555"|2
"51960597"|1

只显示第三列的总和,但我还需要2列。 这种情况我该怎么办?

尝试:

$ awk -F"|" '{ a[ OFS ]+=; b[ OFS ]+=; c[ OFS ]+= }
  END {
    for (i in a) {
      print i, a[i], b[i], c[i];
    }
  }
' OFS=\| file.csv
"201707"|"51976551"|2|0|2
"201707"|"51960597"|1|0|0
"201707"|"51923555"|2|0|2
"201707"|"51955194"|2|0|0

工作原理

  • -F"|"

    这会将输入的字段分隔符设置为 |

  • a[ OFS ]+=; b[ OFS ]+=; c[ OFS ]+=

    这会跟踪第三、第四和第五列的总数。

  • END { for (i in a) { print i, a[i], b[i], c[i]; } }

    这会打印出结果。

  • OFS=\|

    这告诉 awk 使用 | 作为输出的字段分隔符。

要保留顺序:

在END块中处理

awk  'BEGIN{
            FS=OFS="|"
      }
      {
             k =  OFS ; 
             if(!(k in t)){
                    o[++c]=k; 
                    t[k]
             } 
             for(i=3; i<=5; i++)
                    a[k OFS i]+=$i
       }
    END{
             for(i=1; i in o; i++)
             {
                 printf "%s", o[i]; 
                 for(j=3; j<=5; j++)
                     printf "%s%s", OFS, a[o[i] OFS j]; 
                 print ""
              }
        }
     ' infile

或者通过读取同一个文件两次 (GNU awk)

awk  'BEGIN{
        FS=OFS="|"
      }
      function ps(f)
      {
           for(i=3;i<=5;i++)
           if(f)
           { 
                   a[k OFS i]+=$i; 
                   t[k] 
           }else 
                   s=(s ? s OFS :"") a[k OFS i]
       }
       {
         k= OFS 
       }
       FNR==NR{
         ps(1); 
         next
       }
       k in t{
         s=""; 
         ps();  
         print k, s; 
         delete t[k] 
       }
     ' infile infile

输入:

$ cat input
"201707"|"51976551"|1|0|1|"20170702"
"201707"|"51955194"|1|0|0|"20170702"
"201707"|"51923555"|1|0|1|"20170702"
"201707"|"51976551"|1|0|1|"20170703"
"201707"|"51955194"|1|0|0|"20170703"
"201707"|"51923555"|1|0|1|"20170703"
"201707"|"51960597"|1|0|0|"20170703"

输出-1:

$ awk  'BEGIN{FS=OFS="|"}{k =  OFS ; if(!(k in t)){o[++c]=k; t[k]} for(i=3; i<=5; i++)a[k OFS i]+=$i}END{for(i=1; i in o; i++){printf "%s", o[i]; for(j=3; j<=5; j++)printf "%s%s", OFS, a[o[i] OFS j]; print ""}}' infile
"201707"|"51976551"|2|0|2
"201707"|"51955194"|2|0|0
"201707"|"51923555"|2|0|2
"201707"|"51960597"|1|0|0

输出 2:

$ awk  'BEGIN{FS=OFS="|"}function ps(f){for(i=3;i<=5;i++)if(f){ a[k OFS i]+=$i; t[k] }else s=(s ? s OFS :"") a[k OFS i]}{k= OFS }FNR==NR{ps(1); next}k in t{s=""; ps();  print k, s; delete t[k] }' infile infile
"201707"|"51976551"|2|0|2
"201707"|"51955194"|2|0|0
"201707"|"51923555"|2|0|2
"201707"|"51960597"|1|0|0