【问题标题】:awk to print all the minimum values for each categoryawk 打印每个类别的所有最小值
【发布时间】:2022-01-04 03:20:11
【问题描述】:

希望打印基于 $1 和 $3 组合的所有最小值。如果有两条或多条线路可用 对于具有 $1 和 $3 的唯一组合的最小值,则需要打印所有行。例如,$1-"Abc" 的最小值 $3-"10" 出现两次,即 Abc,yyy,10,aaa 和 Abc,ttt,10,aaa。输入文件未按任何顺序排序。

输入.txt

Country,Desc,Amount,Details
Abc,xxx,20,aaa
Abc,yyy,10,aaa
ghi,ttt,25,ccc
Abc,zzz,35,aaa
def,xxx,30,bbb
Abc,ttt,10,aaa
def,yyy,20,bbb
ghi,yyy,25,ccc
def,zzz,45,bbb
ghi,xxx,35,ccc
ghi,zzz,50,ccc

想要打印输出以及标题行NR==1 , {print}

所需的输出.txt

Country,Desc,Amount,Details
Abc,yyy,10,aaa
Abc,ttt,10,aaa
ghi,ttt,25,ccc
ghi,yyy,25,ccc
def,yyy,20,bbb

我正在使用两个命令来获得所需的输出,首先是 sort 基于 $1 和 $3 的输入文件,然后是第二个命令 awk -F, '!seen[$1]++' 寻找您的建议以简单地喜欢一个班轮。

【问题讨论】:

    标签: awk


    【解决方案1】:

    一个处理文件两次的awk。在第一次运行时,它为每个 $1 选择最小的 $3,然后在第二次运行时打印最小的:

    $ awk '
    BEGIN{FS=","}                             # delimiter
    NR==FNR {                                 # first run
        if ($1 in a==0 || $3<a[$1])
            a[$1]=$3
        next
    }
    $3==a[$1] # || FNR==1                     # if Country is data not header
    ' file file                               # uncomment the FNR==1
    Country,Desc,Amount,Details
    Abc,yyy,10,aaa
    ghi,ttt,25,ccc
    Abc,ttt,10,aaa
    def,yyy,20,bbb
    ghi,yyy,25,ccc
    

    【讨论】:

    • 这是一个非常可爱的 awk。
    • 非常感谢詹姆斯·布朗
    【解决方案2】:

    如果您希望输出与相同 Input_file 的顺序相同,请尝试以下操作:

    awk -F, 'NR==1{print;} FNR>1 && FNR==NR{a[$1]=a[$1]>$3?$3:(a[$1]?a[$1]:$3);next} $3==a[$1]'   Input_file  Input_file
    

    编辑:现在也添加非单线形式的解决方案。

    awk -F, 'NR==1{
                    print;
                  }
             FNR>1 && FNR==NR{
                                    a[$1]=a[$1]>$3?$3:(a[$1]?a[$1]:$3);
                                    next
                             }
             $3==a[$1]
            '   Input_file   Input_file
    

    EDIT2:添加一个我们只读取 Input_file 1 次的解决方案。

    awk -F, 'FNR==1{
                    print;
                    next
                   }
             FNR>1{
                    a[$1]=a[$1]>$3?$3:(a[$1]?a[$1]:$3);
                    b[$0]=$0
                  }
             END  {;
                    for(i in b){
                    split(b[i], array,",");
                    if(array[3]==a[array[1]]){
                    print b[i]
                                             }
                               };
                  }
            '   Input_file
    

    【讨论】:

    • 非常感谢 RavinderSingh13
    【解决方案3】:

    如果您想要排序输出,则需要将整个文件保存在内存中(或使用sort)。

    你可以使用红宝石:

    ruby -r csv -e 'mins=Hash.new(2**999)
            data=Hash.new()
            options={:col_sep=>",", :headers=>true, :return_headers=>true}
            data=CSV.parse($<, **options).
                    map { |r| mins[r[0]]=r[2].to_i < mins[r[0]] ? r[2].to_i : mins[r[0]]; r }
            puts data.shift.to_csv(**options)
            data.sort_by { |r| r[0] }.
                    each { |r| if mins[r[0]]==r[2].to_i then puts  r.to_csv(**options) end } 
            ' file
    
    Country,Desc,Amount,Details
    Abc,yyy,10,aaa
    Abc,ttt,10,aaa
    def,xxx,10,bbb
    ghi,yyy,25,ccc
    ghi,ttt,25,ccc
    

    在这种情况下按Country 排序,但您可以对 csv 的任何字段进行排序。

    如果你想要一个排序的版本,你也可以使用gawk

    $ gawk -F"," 'FNR==1{header=$0; next} 
                {lines[FNR-1]=$0
                 if ($1 in mins==0 || $3<mins[$1])
                    mins[$1]=$3             
                }
        
                END{ asort(lines)
                     print header
                     for (e in lines) {
                        split(lines[e], arr, ",")
                        if (mins[arr[1]]==arr[3]) print lines[e]
                     }
                }
    ' file
    # same output
    

    使用gawk,您也可以编写一个简单的排序来对任何键进行排序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-16
      • 2018-08-30
      • 2018-09-05
      • 2013-09-30
      • 1970-01-01
      • 1970-01-01
      • 2017-07-18
      • 1970-01-01
      相关资源
      最近更新 更多