我可以使用哪些标准命令有效地在命令行上打印排序输出的前几行?

时间:2013-02-14 19:52:49

标签: linux bash unix

我基本上想要相当于

... | sort -arg1 -arg2 -... | head -n $k

但是,我的理解是排序将在整个输入上进行O( n log n )。在我的情况下,我正在处理大量数据,因此运行时对我很重要 - 而且我还习惯使用排序临时文件来溢出我的tmp /文件夹。

我宁愿让它使用例如O( n log k )。堆可能会更快,并且还会将工作集内存减少到 k

是否有一些标准命令行工具的组合可以有效地完成这项工作,而我不需要自己编写代码?理想情况下,它将支持sort命令的完全表达排序功能。 sort(至少在ubuntu上)似乎没有人工页面记录的开关来将它拉下来......

3 个答案:

答案 0 :(得分:2)

基于上述情况,还有一些问题,我会说我的问题的正式答案是“没有解决方案”。您可以使用专业工具,也可以使用当前性能的工具,也可以编写自己的工具。

我正在辩论追踪排序源代码并提供补丁。与此同时,如果这个快速入侵代码有助于任何人做与我正在做的事情类似的事情,这就是我为自己写的内容。不是最好的蟒蛇,也是一个非常阴暗的基准:我把它提供给任何关心提供更严格的人:

  • 256个文件,总大小约1.6 Gigs,全部坐在ssd,line上 由\ n,格式行[^ \ t] * \ t [0-9] +
  • 分隔
  • Ubuntu 10.4,6个核心,8个ram,ssd上的/ tmp。
  • $ time sort -t^v<tab> -k2,2n foo* | tail -10000
    • real 7m26.444s
    • 用户7m19.790s
    • sys 0m17.530s
  • $ time python test.py 10000 foo*
    • real 1m29.935s
    • 用户1m28.640s
    • sys 0m1.220s
  • 使用diff进行分析,这两种方法在打破平局方面有所不同,但排序顺序是相同的。

test.py:

#!/usr/bin/env python
# test.py

from sys import argv
import heapq
from itertools import chain

# parse N - the size of the heap, and confirm we can open all input files
N = int(argv[1])
streams = [open(f, "r") for f in argv[2:]]

def line_iterator_to_tuple_iterator(line_i):
    for line in line_i:
        s,c = line.split("\t")
        c = int(c)
        yield (c, s)

# use heap to process inputs
rez = heapq.nlargest(N,
               line_iterator_to_tuple_iterator(chain(*streams)),
               key=lambda x: x[0])

for r in rez:
    print "%s\t%s" % (r[1], r[0])

for s in streams:
    s.close()

答案 1 :(得分:1)

UNIX / Linux提供通才工具集。对于大型数据集,它会加载I / O.它会做你想要的一切,但很慢。如果我们对输入数据有所了解,那将会有很大的帮助。

IMO,你有一些选择,没有你真正喜欢的。

  1. 执行多部分“基数”预排序 - 例如让awk将其键以“A”开头的所有行写入另一个文件“B”,等等。或者,如果您只是'P' 'D'&amp; '问',awk只是吮吸你想要的东西。然后对一小部分进行完整排序。这将创建26个名为A,B ... Z

    的文件

    awk'{print $ 0&gt; substr($ 0,1,1)} bigfile;排序[此处选项] P D Q&gt;结果

  2. 花费$$ :(示例)从iri.com任何其他排序软件购买CoSort。这些排序使用各种优化,但它们不像bash那样免费。您还可以购买一个SSD,它可以将磁盘上的分类速度提高几个数量级。 5000iops现在75000iops。使用TMPDIR变量将tmp文件放在SSD上,只读取和写入SSD。但请使用现有的UNIX工具集。

  3. 使用R或strata之类的软件,或者最好使用数据库;所有这些都适用于大型数据集。

  4. 执行您现在正在执行的操作,但在UNIX排序运行时观察youtube。

  5. IMO,当您想要快速获得结果时,您正在使用错误的工具来处理大型数据集。

答案 2 :(得分:0)

这是一个粗略的部分解决方案:

#!/usr/bin/perl

use strict;
use warnings;

my @lines = ();

while (<>) {
    push @lines, $_;
    @lines = sort @lines;
    if (scalar @lines > 10) {
        pop @lines;
    }
}
print @lines;

它仅读取输入数据一次,持续保持前10行的排序数组。

每次对整个数组进行排序效率很低,当然,但我猜测对于一个千兆字节的输入,它仍然会比sort huge-file | head快得多。

添加一个选项来改变打印的行数就足够了。添加选项以控制排序的完成方式会有点困难,但如果CPAN中的某些内容对我有帮助,我也不会感到惊讶。

更抽象地说,从大型数组中获取前N个排序元素的一种方法是使用部分Quicksort,除非您需要,否则不需要对正确的分区进行排序。这需要将整个数组保存在内存中,这在您的情况下可能是不切实际的。

您可以将输入拆分为中等大小的块,应用一些聪明的算法来获取每个块的前N行,将块连接在一起,然后将相同的算法应用于结果。根据块的大小,sort ... | head可能足够聪明。使用split -l ...将shell脚本放在一起并不困难。

(根据需要插入更多的手势。)

免责声明:我刚试过一个比你正在使用的文件小得多的文件(约170万行),而且我的方法比sort ... | head慢。