【问题标题】:Getting the count of unique values in a column in bash获取bash中列中唯一值的计数
【发布时间】:2011-06-22 18:06:31
【问题描述】:

我有包含几列的制表符分隔文件。我想计算文件夹中所有文件的列中不同值的出现频率,并按计数的递减顺序对它们进行排序(最高计数优先)。我将如何在 Linux 命令行环境中完成此操作?

它可以使用任何常用的命令行语言,如 awk、perl、python 等。

【问题讨论】:

    标签: bash command-line frequency


    【解决方案1】:

    Perl

    此代码计算所有列的出现次数,并为每个列打印一个排序报告:

    # columnvalues.pl
    while (<>) {
        @Fields = split /\s+/;
        for $i ( 0 .. $#Fields ) {
            $result[$i]{$Fields[$i]}++
        };
    }
    for $j ( 0 .. $#result ) {
        print "column $j:\n";
        @values = keys %{$result[$j]};
        @sorted = sort { $result[$j]{$b} <=> $result[$j]{$a}  ||  $a cmp $b } @values;
        for $k ( @sorted ) {
            print " $k $result[$j]{$k}\n"
        }
    }
    

    将文本另存为 columnvalues.pl
    运行为:perl columnvalues.pl files*

    说明

    在顶层while循环中:
    * 循环遍历组合输入文件的每一行
    * 将行拆分为@Fields 数组
    * 对于每一列,递增结果数组哈希数据结构

    在顶层 for 循环中:
    * 循环遍历结果数组
    * 打印列号
    * 获取该列中使用的值
    * 按出现次数对值进行排序
    * 基于值的二级排序(例如 b vs g vs m vs z)
    * 使用排序列表遍历结果哈希
    * 打印每次出现的值和次数

    基于@Dennis 提供的示例输入文件的结果

    column 0:
     a 3
     z 3
     t 1
     v 1
     w 1
    column 1:
     d 3
     r 2
     b 1
     g 1
     m 1
     z 1
    column 2:
     c 4
     a 3
     e 2
    

    .csv 输入

    如果您的输入文件是 .csv,请将 /\s+/ 更改为 /,/

    混淆

    在一场丑陋的比赛中,Perl 装备精良。
    这个单线做同样的事情:

    perl -lane 'for $i (0..$#F){$g[$i]{$F[$i]}++};END{for $j (0..$#g){print "$j:";for $k (sort{$g[$j]{$b}<=>$g[$j]{$a}||$a cmp $b} keys %{$g[$j]}){print " $k $g[$j]{$k}"}}}' files*
    

    【讨论】:

      【解决方案2】:

      这是在 shell 中执行此操作的一种方法:

      FIELD=2
      cut -f $FIELD * | sort| uniq -c |sort -nr
      

      这是 bash 擅长的事情。

      【讨论】:

      • 事物的“排序”... ar ar ar! :)
      • 有点独特的东西。 :P(顺便说一句。使用-d, 以逗号或任何其他分隔符分隔字段)。
      • 我使用了cut -f 1 -d ' '。万分感谢。 :)
      【解决方案3】:

      查看第二列的频率计数(例如):

      awk -F '\t' '{print $2}' * | sort | uniq -c | sort -nr
      

      文件A.txt

      z    z    a
      a    b    c
      w    d    e
      

      文件B.txt

      t    r    e
      z    d    a
      a    g    c
      

      文件C.txt

      z    r    a
      v    d    c
      a    m    c
      

      结果:

        3 d
        2 r
        1 z
        1 m
        1 g
        1 b
      

      【讨论】:

        【解决方案4】:

        Ruby(1.9+)

        #!/usr/bin/env ruby
        Dir["*"].each do |file|
            h=Hash.new(0)
            open(file).each do |row|
                row.chomp.split("\t").each do |w|
                    h[ w ] += 1
                end
            end
            h.sort{|a,b| b[1]<=>a[1] }.each{|x,y| print "#{x}:#{y}\n" }
        end
        

        【讨论】:

        • 这很有趣,因为我使用了它并且它有效,还因为我对 ruby​​ 的丑陋感到惊讶.. 我认为 perl 很糟糕!
        • 在 ruby​​ 的辩护中,这可以真正得到改善。例如,使用each_with_object 等。简而言之,这写得有些粗俗。
        【解决方案5】:

        GNU site 建议使用这个不错的 awk 脚本,它可以打印单词及其频率。

        可能的变化:

        • 您可以通过管道通过sort -nr(以及反向wordfreq[word])以降序查看结果。
        • 如果你想要一个特定的列,你可以省略 for 循环,只写 freq[3]++ - 用列号替换 3。

        这里是:

         # wordfreq.awk --- print list of word frequencies
        
         {
             $0 = tolower($0)    # remove case distinctions
             # remove punctuation
             gsub(/[^[:alnum:]_[:blank:]]/, "", $0)
             for (i = 1; i <= NF; i++)
                 freq[$i]++
         }
        
         END {
             for (word in freq)
                 printf "%s\t%d\n", word, freq[word]
         }
        

        【讨论】:

        • 很棒的示例脚本。它展示了 awk 的很多功能。
        • 这个脚本有助于我确定我真正需要注意的 Excel 工作簿中的哪些行:) (将 Excel 内容复制到文本文件,使用 awk,瞧!,我可以grep -n) 的模式文件。
        猜你喜欢
        • 2017-10-12
        • 2018-03-23
        • 2022-01-18
        • 2019-05-01
        • 1970-01-01
        • 2020-10-09
        • 1970-01-01
        • 2021-04-25
        • 1970-01-01
        相关资源
        最近更新 更多