【问题标题】:Print duplicate count without removing duplicates in Terminal打印重复计数而不删除终端中的重复项
【发布时间】:2017-08-20 21:23:36
【问题描述】:

我是在 mac 上使用终端的新手,并且有一个大的 .tsv 文件,其中包含一个项目列表和旁边的两个值。我希望能够在第一次出现的项目旁边打印重复的数量,而无需删除其他数据。

我知道 cut -f 1 |排序 | uniq -c 但这会删除我想保留以供分析的许多有价值的数据。我正在阅读有关 awk 和 grep 的信息,但我想我需要一点帮助。

这是我正在尝试处理的文件的示例:

fruit   number  reference
apple   12  342
apple   13  345
apple   43  4772
banana  19  234
banana  73  3242
peach   131 53423
peach   234 3266
peach   242 324
peach   131 56758
peaches 29  2434

理想情况下,输出应如下所示:

fruit   number  reference   fruit_count
apple   12  342 3
apple   13  345 
apple   43  4772    
banana  19  234 2
banana  73  3242    
peach   131 53423   4
peach   234 3266    
peach   242 324 
peach   131 56758   
peaches 29  2434    1

这样的事情可能吗?我可以使用公式获得所需的输出 excel,但文件太大并且不断崩溃。任何帮助将不胜感激。

编辑:添加我当前的解决方案(不符合我的要求)

cut -f 1 fruitsample.txt | sort | uniq -c | sed -e 's/ *//' -e 's/ / /'

这给了我预期的计数,将 uniq -c 的标准计数 + 空格输出替换为制表符,但它也会对标题行进行排序并删除第二列和第三列。

在 Excel 上,我可以使用公式 =IF(COUNTIF(A$2:A2,A2)=1,COUNTIF(A:A,A2),"") 并填写它。我正在处理的文件有将近 680K 行数据,而 Excel 在尝试计算这么多行时会卡住。

正如我所提到的,我是一个寻求指导的初学者。我只是对 awk 或 grep 不太熟悉。再次感谢!

【问题讨论】:

  • 我已投票决定关闭此问题,因为它似乎是请求推荐工具或解决方案,而不是请求帮助您自己的代码。这使您的问题与 StackOverflow 无关。如果该评估不正确,并且您确实需要帮助编写自己的代码,请add your work so far to your question,我很乐意撤回我的近距离投票。
  • @ghoti 添加了我正在使用的命令。
  • 关闭投票被撤回。

标签: unix awk terminal gnu-coreutils


【解决方案1】:

awk 来救援!

awk 'NR==FNR {a[$1]++; next} 
     FNR==1  {print $0, "fruit_count"; next} 
     $1 in a {$(NF+1)=a[$1]; delete a[$1]}1' file{,} | 
column -t

fruit    number  reference  fruit_count
apple    12      342        3
apple    13      345
apple    43      4772
banana   19      234        2
banana   73      3242
peach    131     53423      4
peach    234     3266
peach    242     324
peach    131     56758
peaches  29      2434       1

为了解释主要思想,我将使用一个更简单的结构,没有标题和未排序的数据

$ cat file
apple
banana
apple
apple
cherry
banana

$ awk 'NR==FNR {a[$1]++; next}            # in the first pass, save key counts
                $1 in a                   # if the key in map
                        {$(NF+1)=a[$1];   # add the count as a last column
                         delete a[$1]}    # remove key from map
                1                         # print
       ' file{,} |                        # bash shorthand for: file file
  column -t                               # pretty print columns 


apple   3
banana  2
apple
apple
cherry  1
banana

对于简化的示例,使用 unix 工具链可以实现相同的效果

join -a1 -11 -22 -o1.2,2.1 <(cat -n file) <(cat -n file | sort -k2 | uniq -c -f1)

添加标题将需要更多的杂耍;这是awk 闪耀的地方。

【讨论】:

  • 如果你知道类似的帖子,你能把它标记为骗子吗?如果没有,请在您的答案中添加解释。我无法理解您如何从问题中的代码到您的答案中包含的内容。 :-P
  • 写出来比搜索副本更容易。出于某种原因,SO不允许您在自己的答案中搜索(或者我不知道如何)。
  • 你的意思是,像this?
  • #SOreadytohelp! :-D 我会喜欢这种难得的经历。我很少能教你任何东西!
  • 我做错了吗?我刚试过这个,但它没有返回任何输出。 :( 我试过$ awk 'NR=FNR {a[$1]++; next} FNR==1 {print $0, "fruit_count"; next} $1 in a {$(NF+1)=a[$1]}1' fruitysample.txt,结果是空的。
【解决方案2】:

另一个使用 awk 和 double-tacs:

$ tac file | awk '
NR>1 {print q, (p==$1?"":++c)}                  # p previous first field, q previous record
     {c=(p==$1?c+1:0); p=$1; q=$0}              # c is the counter
END  {print q, "fruit_count"}
' | tac
fruit   number  reference fruit_count
apple   12  342 3
apple   13  345
apple   43  4772
banana  19  234 2
banana  73  3242
peach   131 53423 4
peach   234 3266
peach   242 324
peach   131 56758
peaches 29  2434 1

【讨论】:

    【解决方案3】:

    这在输入文件的单次传递中执行您想要的操作,并且一次仅将 1 个水果的值存储在内存中,因此尽管您的文件对于 MS-Excel 来说太大,但它不应该有性能或内存问题:

    $ cat tst.awk
    NR==1 { print $0, "fruit_count"; next }
    $1 != prev { prt() }
    { arr[++cnt] = $0; prev = $1 }
    END { prt() }
    function prt(    i) {
        if (cnt) {
            print arr[1], cnt
            for (i=2; i <= cnt; i++) {
                print arr[i]
            }
            delete arr
            cnt = 0
        }
    }
    
    $ awk -f tst.awk file | column -t
    fruit    number  reference  fruit_count
    apple    12      342        3
    apple    13      345
    apple    43      4772
    banana   19      234        2
    banana   73      3242
    peach    131     53423      4
    peach    234     3266
    peach    242     324
    peach    131     56758
    peaches  29      2434       1
    

    【讨论】:

    • 谢谢!这可行,但这是否需要先对文件进行排序?我在另一篇文章中发现sort within awk ignoring the header 可以通过管道将其通过,例如NR&lt;2{print $0;next}{print $0| "sort -r"} 我再次将第一行写为NR&lt;2 { print $0, "fruit_count"; next }{ print $0|"sort"},但它只是将原始输出下的排序数据与计数堆叠在一起。
    • 不一定要排序,但要分组,以便具有相同键 ($1) 的所有行都是连续的,就像您提供的数据排列方式示例一样。如果这不是您的数据的样子,那么您当然应该编辑您的问题,以便您的示例更好地反映您的真实数据,因此我们不会试图帮助您解决与实际问题不同的问题!
    • 我想更多地了解这个程序在做什么。根据我的理解,第一行打印标题,然后第二部分开始循环检查当前项目是否与前一个相同,如果是,则增加计数。那是对的吗?我不完全确定中间的循环在做什么for (i=2; i &lt;= cnt; i++) 为什么将 i 的值设置为 2 来启动循环?
    • 你已经接受了一个答案,所以我们会鞭打一匹死马......你可以从 Arnold Robbins 的《Effective Awk programming, 4th Edition》一书中学习 awk 语法和语义。
    • 感谢@ed-morton 的推荐!
    猜你喜欢
    • 2018-05-24
    • 1970-01-01
    • 2021-02-25
    • 2014-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多