【问题标题】:Unix, group rows and sum values of columns from file.csvUnix,file.csv 中的组行和列的总和值
【发布时间】:2018-06-28 04:24:12
【问题描述】:

我有这个文件.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[$2] += $3 }
  END {
    for (i in a) {
      printf "%s|%s\n", i, a[i];
    }
  }
'

结果是:

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

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

【问题讨论】:

    标签: csv unix awk summarize


    【解决方案1】:

    为了保持秩序:

    在END块中处理

    awk  'BEGIN{
                FS=OFS="|"
          }
          {
                 k = $1 OFS $2; 
                 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=$1 OFS $2
           }
           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 = $1 OFS $2; 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=$1 OFS $2}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
    

    【讨论】:

      【解决方案2】:

      试试:

      $ awk -F"|" '{ a[$1 OFS $2]+=$3; b[$1 OFS $2]+=$4; c[$1 OFS $2]+=$5 }
        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[$1 OFS $2]+=$3; b[$1 OFS $2]+=$4; c[$1 OFS $2]+=$5

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

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

        这会打印出结果。

      • OFS=\|

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

      【讨论】:

      • 最好定义key=$1 FS $2并改用它。
      • @karakfa 很好的建议!谢谢。 (根据您的评论,我怀疑OFS 是更好的选择)。答案已更新。
      • 这里没关系,因为它们是相同的,但一般来说,OFS 不会保证复合键是唯一的。而 FS 保证这一点。
      • 感谢 John1024 和 karafka,FS 更好,它正确显示了每列的总和!你救了我!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-26
      • 1970-01-01
      • 2022-07-21
      • 1970-01-01
      • 2022-10-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多