awk 计算唯一出现次数并打印其他列

awk count unique occurrences and print other columns

我有如下一段代码:

awk '{h[]++}; END { for(k in h) print k, h[k]}' ${infile} >> ${outfile2}

这是我想要的一部分:打印出唯一值,然后计算这些唯一值出现的次数。现在,我还想从每个唯一值中打印出第 2 列和第 3 列。由于某种原因,以下内容似乎不起作用:

awk '{h[]++}; END { for(k in h) print k, , , h[k]}' ${infile} >> ${outfile2}
awk '{h[]++}; END { for(k in h) print k, h[], h[], h[k]}' ${infile} >> ${outfile2}

第一个打印出最后一个索引的第 2 和第 3 列,而第二个代码打印出除了 k 和 h[k] 之外的任何内容。

${infile} 看起来像:

20600        33.8318 -111.9286       -1     0.00        0
20600        33.8318 -111.9286       -1     0.00        0
30900        33.3979 -111.8140       -1     0.00        0
29400        33.9455 -113.5430       -1     0.00        0
30600        33.4461 -111.7876       -1     0.00        0
20600        33.8318 -111.9286       -1     0.00        0
30900        33.3979 -111.8140       -1     0.00        0
30600        33.4461 -111.7876       -1     0.00        0

所需的输出将是:

20600, 33.8318, -111.9286, 3
30900, 33.3979, -111.8140, 2
29400, 33.9455, -113.5430, 1
30600, 33.4461, -111.7876, 2

GNU datamash 是一个非常方便的工具,用于处理文件中的柱状数据组,使这项工作变得微不足道。

假设您的文件使用制表符分隔各列,就像它看起来的那样:

$ datamash -s --output-delimiter=, -g 1,2,3 count 3 < input.tsv
20600,33.8318,-111.9286,3
29400,33.9455,-113.5430,1
30600,33.4461,-111.7876,2
30900,33.3979,-111.8140,2

虽然在awk中并没有复杂多少,但是使用多维数组:

$ awk 'BEGIN { OFS=SUBSEP="," }
       { group[,,]++ }
       END { for (g in group) print g, group[g] }' input.tsv
29400,33.9455,-113.5430,1
30600,33.4461,-111.7876,2
20600,33.8318,-111.9286,3
30900,33.3979,-111.8140,2

如果你想要排序输出而不是随机顺序,如果使用 GNU awk,请在 BEGIN 块中添加一个 PROCINFO["sorted_in"] = "@ind_str_asc",或者通过 sort 管道输出.

您也可以通过流水线化一堆实用程序(包括 awk 和 uniq)来获得相同的效果:

$ sort -k1,3n input.tsv | cut -f1-3 | uniq -c | awk -v OFS=, '{ print , , ,  }' 
20600,33.8318,-111.9286,3
29400,33.9455,-113.5430,1
30600,33.4461,-111.7876,2
30900,33.3979,-111.8140,2

你很接近,你可以在 awk 中完成所有操作,但如果你要存储基于字段 1 的计数,并且在 END 中还可以使用字段 2 和字段 3输出,您还需要将字段 2 和 3 存储在由字段 1(或您正在计算的任何字段)索引的数组中。例如你可以这样做:

awk -v OFS=', ' '
    { h[]++; i[]=; j[]= }
    END { 
        for (a in h)
            print a, i[a], j[a], h[a]
        }
' infile

其中 h[] 保存字段 1 被视为索引数组与字段 1 的次数计数。i[]= 捕获由字段 1 索引的字段 2,然后 j[]=捕获由字段 1 索引的字段 3。

然后在END内只需要输出字段1(a h的索引),i[a](字段2),j[a](字段 3),最后 h[a] 看到字段 1 的次数。

例子Use/Output

使用您的示例数据,您可以 copy/middle-mouse-paste 终端上的代码使用正确的文件名,例如

$ awk -v OFS=', ' '
>     { h[]++; i[]=; j[]= }
>     END {
>         for (a in h)
>             print a, i[a], j[a], h[a]
>         }
> ' infile
20600, 33.8318, -111.9286, 3
29400, 33.9455, -113.5430, 1
30600, 33.4461, -111.7876, 2
30900, 33.3979, -111.8140, 2

它提供了所需的输出。如果您需要按照您显示的输出顺序保留记录的顺序,您可以使用字符串连接将字段 1、2 和 3 分组为数组的索引,然后输出数组和索引,例如

$ awk '{a[", "", "]++}END{for(i in a) print i ", " a[i]}' infile
20600, 33.8318, -111.9286, 3
30600, 33.4461, -111.7876, 2
29400, 33.9455, -113.5430, 1
30900, 33.3979, -111.8140, 2

检查一下,如果您还有其他问题,请告诉我。