【问题标题】:awk: find smallest 10 values from columnawk:从列中找到最小的 10 个值
【发布时间】:2016-12-03 12:13:53
【问题描述】:

我有一个这样排列的文件:

a  123
b 45
c -23
d 166

我是 awk 的新手,我想在第 2 列中找到十个(或 x 个)最小的数字并将这些行打印到一个新文件中。

目前我们使用以下代码并观察结果,看看我们是否有大约十个。如果不是,我们将 -5.0 更改为其他内容,依此类推:

cat input.txt | awk '{if($2 < -5.0) {print $1" "$2}}' >> output.txt

【问题讨论】:

  • 如果列表中包含多条相同编号的记录(在最低记录中),您要保留第一条记录还是最后一条记录?

标签: awk


【解决方案1】:

您可以在没有 AWK 的情况下轻松做到这一点:

$ sort -nk 2 input.txt | head -10 > output.txt

它对输入文件进行排序并打印前 10 行(第 2 列中的最低值)。如果您的输入文件特别大,这可能不适合。这个sort 可能使用了 O(N log N) 排序算法(对于 N 行输入)。


如果您只想要最低限度,AWK 中的解决方案将是单线。在这种情况下有点棘手,因为您必须保持多个最低值。试试这样的:

lowest.awk

#!/usr/bin/awk -f
BEGIN{if (X=="") X=10; s=0}
{ # insert new value in order
    for (i=0; i<s && $2>a[i]; ++i);
    if (s==X && i==s) next
    for (j=s; j>i; --j) {
        a[j] = a[j-1]
        b[j] = b[j-1]
    }
    a[i] = $2
    b[i] = $0
    if (s<X) s++
}
END{ # print stored lines
    for (i=0; i<s; ++i)
        print b[i]
}

在命令行上运行:

$ awk -f lowest.awk infile.txt > outfile.txt

您可以使用-v X=10 指定要从命令行打印的最低值的数量X

$ awk -v X=10 -f lowest.awk infile.txt > outfile.txt

但 10 是默认值。

这会将每个值(在第 2 列中)与数组 a 的每个元素进行比较,并在需要的地方插入新值。数组b 存储要在末尾打印的整行 ($0)。 ab 的(使用)大小是 s

通常,您应该使用 for (i in a) 之类的内容遍历数组中的条目,但在这种情况下,a[s] 处有一个额外条目,在打印结果时必须忽略它,因此采用 for (i=0; i&lt;s; ++i) 格式在END 块中。

在最坏的情况下,每个 N 个值都会与 a 中的 X 个值进行比较。所以这是 O(XN),这是对 O(N log N) sort 版本的改进。此外,它需要更少的内存,因为您只在内存中存储 O(X) 个值而不是所有 N 行。

请注意,这通过插入来维护顺序。在 X=N 的地方,您保留所有值并按顺序列出它们——换句话说就是排序。当 X 接近 N,O(XN) 公式接近 O(N2) >,这是插入排序算法的复杂度。所以这个 AWK 版本只比基于 O(N log N) 排序的方法更有效,其中 X 远小于 N.

【讨论】:

  • UUoC — cat 的无用使用。 sort 命令将读取文件,这样可以节省将可能很大的文件推送到两个管道之一。
  • @JonathanLeffler 好点。我从复制OP的行开始。另外值得一提的是,AWK 也可以将输入文件作为参数。
  • 很容易上当受骗去做一些不太令人满意的事情,因为 OP 做了同样的事情——我以类似的方式被抓了很多次。最近少了;我现在知道这个问题了。
【解决方案2】:

@e0k 的sort|head 通常是正确的 UNIX 解决方案,但如果您出于某种原因必须使用 awk,那么使用 GNU awk 来进行 sorted_in:

$ awk -v x=2 '{a[NR]=$2; b[NR]=$0}
    END{ PROCINFO["sorted_in"]="@val_num_asc"; for (i in a) if (x-->0) print b[i] }' file
c -23
b 45

$ awk -v x=3 '{a[NR]=$2; b[NR]=$0}
    END{ PROCINFO["sorted_in"]="@val_num_asc"; for (i in a) if (x-->0) print b[i] }' file
c -23
b 45
a  123

【讨论】:

  • 这比我的 AWK 版本更简洁(也更容易阅读)。它的作用与sort|head 基本相同,将整个输入文件存储在内存中,对其进行排序,然后打印第一行x。我知道PROCINFO["sorted_in"] 设置了迭代顺序,但是AWK 什么时候真正对它进行排序?当你使用in 时它会排序吗?或者它是否将键放在哈希表中,以便以后可以轻松地将它们按顺序取出?根据其实现方式,使用哈希表进行这样的排序可能会很快(但会使用更多内存)。
  • 它会在您执行 in 时对其进行排序,因为在您设置“sorted_in”之前,它甚至不知道您希望它按什么顺序排序,您可以在实际使用它之前设置 20 次。您必须查看 gawk 手册和/或联系实施者以了解更多实施细节。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-29
  • 2018-04-06
  • 1970-01-01
  • 2021-10-10
  • 2013-03-09
  • 1970-01-01
相关资源
最近更新 更多