【问题标题】:Counting the number of unique values based on more than two columns in bash根据bash中的两列以上计算唯一值的数量
【发布时间】:2021-06-16 12:22:03
【问题描述】:

我需要修改以下代码以处理多个列。

Counting the number of unique values based on two columns in bash

awk '                  ##Starting awk program from here.
BEGIN{
  FS=OFS="\t"
}
!found[$0]++{       ##Checking condition if 1st and 2nd column is NOT present in found array then do following.
  val[$1]++            ##Creating val with 1st column inex and keep increasing its value here.
}
END{                   ##Starting END block of this progra from here.
  for(i in val){       ##Traversing through array val here.
    print i,val[i]     ##Printing i and value of val with index i here.
  }
}
'  Input_file          ##Mentioning Input_file name here.

计算每个双精度数的表(所有 DIS)

  patient  sex    DISa  DISb  DISc  DISd   DISe   DISf  DISg  DISh  DISi
patient1 male   550.1 550.5 594.1 594.3  594.8  591   1019  960.1 550.1
patient2 female 041   208   250.2 276.14 426.32 550.1 550.5 558   041  
patient3 female NA    NA    NA    NA     NA     NA    NA    041   NA 

我需要的输出是:

550.1    3
550.5    2
594.1    1
594.3    1
594.8    1
591    1
1019    1
960.1    1
550.1    1
041    3
208    1
250.2    1
276.14    1
426.32    1
558    1

【问题讨论】:

    标签: awk sed


    【解决方案1】:

    考虑一下awk

    awk -v OFS='\t' 'NR > 1 {for (i=3; i<=NF; ++i) if ($i+0 == $i) ++fq[$i]} END {for (i in fq) print i, fq[i]}' file
    
    276.14  1
    960.1   1
    594.3   1
    426.32  1
    208 1
    041 3
    594.8   1
    550.1   3
    591 1
    1019    1
    558 1
    550.5   2
    250.2   1
    594.1   1
    

    更易读的形式:

    awk -v OFS='\t' '
    NR > 1 {
       for (i=3; i<=NF; ++i)
          if ($i+0 == $i)
             ++fq[$i]
    }
    END {
       for (i in fq)
          print i, fq[i]
    }' file
    

    $i+0 == $i 是用于确保列值是数字的检查。

    【讨论】:

      【解决方案2】:

      如果必须保留顺序,那么您需要一个额外的数组b[] 来保持遇到每个数字的顺序,例如

      awk '
          BEGIN { OFS = "\t" }
          FNR > 1 { 
              for (i=3;i<=NF;i++)
                  if ($i~/^[0-9]/) { 
                      if (!($i in a))
                          b[++n] = $i;
                      a[$i]++
                  }
          }
          END {
              for (i=1;i<=n;i++)
                  print b[i], a[b[i]]
      }' file
      

      使用/输出示例

      $ awk '
      >     BEGIN { OFS = "\t" }
      >     FNR > 1 {
      >         for (i=3;i<=NF;i++)
      >             if ($i~/^[0-9]/) {
      >                 if (!($i in a))
      >                     b[++n] = $i;
      >                 a[$i]++
      >             }
      >     }
      >     END {
      >         for (i=1;i<=n;i++)
      >             print b[i], a[b[i]]
      > }' patients
      550.1   3
      550.5   2
      594.1   1
      594.3   1
      594.8   1
      591     1
      1019    1
      960.1   1
      041     3
      208     1
      250.2   1
      276.14  1
      426.32  1
      558     1
      

      如果您还有其他问题,请告诉我。

      【讨论】:

      • 是的,这是保持键原始顺序的好方法
      • 我不确定它是否需要。我想我也应该更新OFS
      【解决方案3】:

      从以上 2 个答案(@anubhava 和 @David)中获取完整的解决方案,只需在他们的解决方案中添加一些调整(根据显示的 OP 示例在此处应用整数值检查)并在此处添加 2 个解决方案。仅使用所示示例编写和测试。

      第一种解决方案:如果输出中的顺序无关紧要,请尝试:

      awk -v OFS='\t' '
      NR > 1 {
         for (i=3; i<=NF; ++i)
            if (int($i))
               ++fq[$i]
      }
      END {
         for (i in fq)
            print i, fq[i]
      }' Input_file
      


      第二个解决方案:如果根据大卫的回答顺序很重要,请尝试。

      awk '
          BEGIN { OFS = "\t" }
          FNR > 1 { 
              for (i=3;i<=NF;i++)
                  if (int($i)) { 
                      if (!($i in a))
                          b[++n] = $i;
                      a[$i]++
                  }
          }
          END {
              for (i=1;i<=n;i++)
                  print b[i], a[b[i]]
      }' Input_file
      

      【讨论】:

      • 大多数 OP 数值不是整数,并且他的一些非数值包含数字,将它们转换为整数并测试结果对于像 0.5 这样的数字或类似的字符串会失败1foo。一个数字的测试是($i+0 == $i),就像在@anubhavas 代码中一样。
      • @EdMorton,谢谢先生,如果我对此有更多想法,我会尝试编辑它们(因为 anubhava 已经涵盖了),谢谢。
      【解决方案4】:

      将 GNU awk 用于多字符 RS:

      $ awk -v RS='[[:space:]]+' '$0+0 == $0' file | sort | uniq -c
            3 041
            1 1019
            1 208
            1 250.2
            1 276.14
            1 426.32
            3 550.1
            2 550.5
            1 558
            1 591
            1 594.1
            1 594.3
            1 594.8
            1 960.1
      

      如果字段的顺序真的很重要,只需将上面的内容通过管道传递给awk '{print $2, $1}'

      【讨论】:

        猜你喜欢
        • 2020-10-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-13
        • 2021-11-22
        • 1970-01-01
        • 2019-08-27
        相关资源
        最近更新 更多