【问题标题】:awk count unique occurrences and print other columnsawk 计算唯一出现次数并打印其他列
【发布时间】:2019-08-16 19:34:11
【问题描述】:

我有以下代码:

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

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

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

第一个打印出最后一个索引的第二和第三列,而第二个代码除了 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

【问题讨论】:

  • 您必须存储一行的整个值,以便您可以在 END 块内将其打印出来。 $2$3 仅来自读取的最后一行输入,但您的 h[](数组)将包含许多条目。认为您将需要另一个数组,以便您可以从您的 uniq 列表中引用正确的 NR(但现在没有时间对此进行测试)。祝你好运。

标签: awk uniq


【解决方案1】:

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

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

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

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

使用/输出示例

使用您的示例数据,您可以使用正确的文件名在终端复制/鼠标中键粘贴代码,例如

$ awk -v OFS=', ' '
>     { h[$1]++; i[$1]=$2; j[$1]=$3 }
>     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[$1", "$2", "$3]++}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

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

【讨论】:

  • 谢谢。我添加了一个分组来保留执行相同操作的输出顺序,但在使用索引连接时有点不典型。
【解决方案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[$1,$2,$3]++ }
       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 $2, $3, $4, $1 }' 
20600,33.8318,-111.9286,3
29400,33.9455,-113.5430,1
30600,33.4461,-111.7876,2
30900,33.3979,-111.8140,2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多