当第一个键已经排序时,对 N 个键的数据集进行最佳排序

当第一个键已经排序时,对 N 个键的数据集进行最佳排序

我经常遇到需要对部分有序数据进行排序的情况。第一列已经排序,后面的列尚未排序。就像这个两列示例:

1 5
1 3
2 10
2 -1
2 3
3 11
3 -200
3 20

期望的输出是

sort -k 1,1g -k 2,2g

这种方法可行,但存在一个问题,即在读取完所有输入之前,排序不会产生任何结果。当输入是几 GB 的文本时,这可能需要一段时间,在此期间,管道中排序的下游部分无法执行。从内存使用率来看,这种方法也不是很高效,因为整个数据集必须同时驻留在其中,尽管为了实现所需的排序,实际上只需要一小部分数据就足够了。

使用脚本,将其分解成块然后对每个块进行排序并不困难。sort 命令是否有一个选项可以通知它该列中的数据已经排序?我在 sort 8.4 中没有看到它,但也许我只是错过了它?

如果排序在已排序的列中遇到无序值,则排序应退出。这表示上游处理中出现错误。

答案1

我无法用 sole 做到这一点sort。这可能行不通。

在我的解决方案中,awk处理第一列并sort根据需要运行多次。脚本从 stdin 获取输入,然后打印到 stdout。

#!/usr/bin/awk -f

BEGIN { command = "sort -k 2,2g" }

{
if ( NR==1 ) {
   val=$1
   buf=$0
}
else
if ( $1 < val ) {
   print "Unsorted 1st column detected. Processing last valid chunk and aborting." > "/dev/stderr"
   exit 1
   }
else {
if ( $1 == val )
   buf=buf"\n"$0
else
   {
   print buf | command
   close(command)
   buf=$0
   val=$1
   }
   }
}

END { print buf | command }

笔记:

  • close(command)至关重要。没有它,所有管道command都会通向单身的 sort
  • 我认为awk的比较运算符可以很好地处理数字。真的确保解决方案能够正常sort工作,您需要分别检索 和sort -c -k 1,1g的退出状态,然后在此基础上构建脚本逻辑。这将在每个输入行上运行两个进程,我预计性能会受到很大影响。val"\n"$1$1"\n"valsort

答案2

Perl 来救援!

它完全按照您的要求进行操作,它将以相同数字开头的输入块传输到仅对第二列进行操作的排序。

#!/usr/bin/perl
use warnings;
use strict;

my $sort;

my $first = -1;
while (<>) {
    my ($x, $y) = split;
    if ($first != $x) {
        die "Unsorted line $." if $first > $x;
        $first = $x;
        open $sort, '|-', 'sort -k2,2n' or die $!;
    }
    print {$sort} $_;
}

唯一的问题可能是初始值$first:如果您的输入在第一列以负数开头,则需要指定一个较小的值。

我使用了n排序而不是g因为它在我的计算机上似乎更快一些。

答案3

Kamil Cuk 在另一个帖子中发布了这个答案。我现在要尝试删除那个帖子,因为显然在两个地方都发布这个答案是不行的,我想在这里保留这个答案。

以下脚本将具有相同第一列的行打印到临时文件中并对其进行排序。

file=$1
# create temporary file
temp=$(mktemp)
trap 'rm "$temp"' EXIT
i_last=""
while read -r i j; do
    if [ "$i" != "$i_last" ]; then
        # output sorted temporary file
        sort -n $temp
        # and truncate temporary file
        > $temp
        # increment first column pointer
        i_last=$i
    fi
    # print all lines into temporary file
    echo "$i" "$j" >>$temp
done <"$file"
# dont forget leftovers
sort -n "$temp"

相关内容