【问题标题】:LINUX Shell script to convert rows to multiple columns将行转换为多列的 LINUX Shell 脚本
【发布时间】:2017-10-30 09:37:08
【问题描述】:

Shell 脚本将行转换为多列

输入 CSV 文件:

Driver Id,Driver Name,Measure Names,Measure Values
123,XYZ,Total Offers,10
123,XYZ,Driver Reject,0
123,XYZ,Driver Accept ,4
123,XYZ,Expired Offers,3
123,XYZ,Total Bookings,6
123,XYZ,Rider Cancels,2
123,XYZ,Driver Cancels,0
123,XYZ,Rider No-Show,0
123,XYZ,Completed Rides,4
124,PQR,Total Offers,2
124,PQR,Driver Reject,0
124,PQR,Driver Accept ,1
124,PQR,Expired Offers,1
124,PQR,Total Bookings,1
124,PQR,Rider Cancels,0
124,PQR,Driver Cancels,0
124,PQR,Rider No-Show,0
124,PQR,Completed Rides,1

需要输出:

司机 ID、司机姓名、总报价、司机拒绝、司机接受、过期报价、总预订量、乘客取消、司机取消、乘客未出现、已完成的行程

123,XYZ,10,0,4,3,6,2,0,0,4
124,PQR,2,0,1,1,1,0,0,0,1

我尝试使用 awk,但结果不正确。

awk -F\, '
    BEGIN{
        P["Total Offers"]="%s;%s;%s;;;;;;;;;\n"
       P["Driver Reject"]="%s;%s;;%s;;;;;;;;\n"
       P["Driver Accept"]="%s;%s;;;%s;;;;;;;\n"
      P["Expired Offers"]="%s;%s;;;;%s;;;;;;\n"
      P["Total Bookings"]="%s;%s;;;;;%s;;;;;\n"     
       P["Rider Cancels"]="%s;%s;;;;;;%s;;;;\n"
      P["Driver Cancels"]="%s;%s;;;;;;;%s;;;\n"     
       P["Rider No-Show"]="%s;%s;;;;;;;;%s;;\n"     
     P["Completed Rides"]="%s;%s;;;;;;;;;%s;\n" 
        }                         
    FNR==1{
        print "Driver Id,Driver Name,Total Offers,Driver Reject,Driver Accept,Expired Offers,Total Bookings,Rider Cancels,Driver Cancels,Rider No-Show,Completed Rides"
        next
        }
    {
        printf(P[$3],$1,$2,$4)
        }
    ' sample1.csv

有人可以帮助我或向我展示任何其他方法来实现这一点。 提前致谢

【问题讨论】:

  • 这些行是否总是按Driver Id,Driver Name 排序?
  • 不一定

标签: linux shell unix awk nawk


【解决方案1】:

考虑到您的 Input_file 与显示的示例相同,如果您不关心输出序列应该作为输入,那么以下内容可能对您有所帮助。

awk -F, 'FNR>1{a[$1,$2]=a[$1,$2]?a[$1,$2] FS $NF:$NF} END{for(i in a){print i FS a[i]}}' SUBSEP=","   Input_file

【讨论】:

    【解决方案2】:

    下面的一个负责输出的顺序以及缺失值,如果有的话

    awk '
         BEGIN{
           FS=OFS=SUBSEP=","; 
         }
         FNR==1{
            printf("%s%s%s",$1,OFS,$2);
            next
         }
         {
          if(!(($1,$2) in tmp)){
            usr[++u] = $1 OFS $2
            tmp[$1,$2]
          }
          if(!($3 in tmp)){
            names[++n] = $3;
            tmp[$3]
            printf("%s%s",OFS,$3)
          }
           arr[$1,$2,$3] = $4
         }
         END{
           print ""
           for(u=1; u in usr; u++){
               printf("%s", usr[u]);
               for(n=1; n in names; n++){
                   indexkey = usr[u] SUBSEP names[n]
                   printf("%s%s",OFS, (indexkey in arr) ? arr[indexkey]:"")
               }
               print ""
           }
         }
        ' infile
    

    说明:

    • FS=OFS=SUBSEP=","; - 将字段分隔符、输出字段分隔符和内置变量 subsep 设置为逗号,在当前程序中至少 OFS 和 SUBSEP 应该相同,因为我使用它访问数组 indexkey = usr[u] SUBSEP names[n],所以如果你有任何其他输入字段分隔符(比如管道)然后制作FS="|"; OFS=SUBSEP=","

    • FNR==1{ printf("%s%s%s",$1,OFS,$2); next } 如果是第一行,则打印前 2 个字段并转到下一行

    • if(!(($1,$2) in tmp)){ usr[++u] = $1 OFS $2 tmp[$1,$2] } 由于您想要有序输出,因此在此程序中使用连续(按顺序)数组 (usr)。 tmp 是数组,其中索引为$1$2usr 是数组,其中索引为变量u,值为$1$2if(!(($1,$2) in tmp)) 处理如果没有以前不存在。

    • if(!($3 in tmp)){ names[++n] = $3; tmp[$3] printf("%s%s",OFS,$3) } 与上面类似,names 数组是连续的,值为 $3

    • arr[$1,$2,$3] = $4数组arr键是3个字段,$1,$2,$3和值是$4

    • 最后在END块循环遍历usrnames数组,建立indexkey并打印数组值,如果indexkey存在于数组arr

    输入:

    $ cat infile
    Driver Id,Driver Name,Measure Names,Measure Values
    123,XYZ,Total Offers,10
    123,XYZ,Driver Reject,0
    123,XYZ,Driver Accept ,4
    123,XYZ,Expired Offers,3
    123,XYZ,Total Bookings,6
    123,XYZ,Rider Cancels,2
    123,XYZ,Driver Cancels,0
    123,XYZ,Rider No-Show,0
    123,XYZ,Completed Rides,4
    124,PQR,Total Offers,2
    124,PQR,Driver Reject,0
    124,PQR,Driver Accept ,1
    124,PQR,Expired Offers,1
    124,PQR,Total Bookings,1
    124,PQR,Rider Cancels,0
    124,PQR,Driver Cancels,0
    124,PQR,Rider No-Show,0
    124,PQR,Completed Rides,1
    

    输出:

    $ awk '
         BEGIN{
           FS=OFS=SUBSEP=","; 
         }
         FNR==1{
            printf("%s%s%s",$1,OFS,$2);
            next
         }
         {
          if(!(($1,$2) in tmp)){
            usr[++u] = $1 OFS $2
            tmp[$1,$2]
          }
          if(!($3 in tmp)){
            names[++n] = $3;
            tmp[$3]
            printf("%s%s",OFS,$3)
          }
           arr[$1,$2,$3] = $4
         }
         END{
           print ""
           for(u=1; u in usr; u++){
               printf("%s", usr[u]);
               for(n=1; n in names; n++){
                   indexkey = usr[u] SUBSEP names[n]
                   printf("%s%s",OFS, (indexkey in arr) ? arr[indexkey]:"")
               }
               print ""
           }
         }
        ' infile
    Driver Id,Driver Name,Total Offers,Driver Reject,Driver Accept ,Expired Offers,Total Bookings,Rider Cancels,Driver Cancels,Rider No-Show,Completed Rides
    123,XYZ,10,0,4,3,6,2,0,0,4
    124,PQR,2,0,1,1,1,0,0,0,1
    

    【讨论】:

    • 感谢 Akshay ...它在上述文件中运行良好...但是当我在 csv 输入文件上检查它时,它没有给出预期的结果
    • 您提供的输入仅为csv文件
    • 是的,它适用于小文件,不适用于像下面这样的大文件。 drive.google.com/file/d/0B4gv_Iku88JxOEdWeUl3X1JHcWc/…你能检查一次吗
    • @user2207709 你的文件有\r char,就在FNR==1 之前请粘贴{ gsub(/\r/,"") } 或者使用dos2unix 更正你的输入文件
    【解决方案3】:

    如果行在必填字段中未排序,则必须使用关联数组。

    $ awk -F, -v cols='Total Offers,Driver Reject,Driver Accept ,Expired Offers,Total Bookings,Rider Cancels,Driver Cancels,Rider No-Show,Completed Rides' '
       BEGIN {n=split(cols,f)} 
       NR>1  {k=$1 FS $2; keys[k]; a[k,$3]=$4} 
       END   {for(k in keys) 
                 {printf "%s", k; 
                  for(i=1;i<=n;i++) printf "%s%d", FS,+a[k,f[i]]; 
                  print ""}}' file
    
    124,PQR,2,0,1,1,1,0,0,0,1
    123,XYZ,10,0,4,3,6,2,0,0,4
    

    如果缺少任何度量行,这将引起注意

    ps。请注意,“Driver Accept”有一个尾随空格,我保留了它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-05
      • 2013-11-07
      • 1970-01-01
      • 1970-01-01
      • 2014-01-19
      • 1970-01-01
      相关资源
      最近更新 更多