【问题标题】:Sort a Huge file对大文件进行排序
【发布时间】:2013-05-15 13:38:44
【问题描述】:

我想对大约 20M 行的大文件进行排序:

  • 团队名称和升序
  • 然后按最高分递减。

这样我就可以获得每支球队得分最高的球员。

我想考虑系统的资源。所以...

  1. 有没有办法在不将所有数据放入 Perl 中的哈希/数组的情况下做到这一点?
  2. 我们可以使用 Unix/Linux 排序实用程序来执行此操作吗?

如果是这样,你能告诉我怎么做吗?

我的输入文件大约有 20M 行,格式如下

Chicago Bulls|Michael Jordan|38
LA Lakers|Kobe Bryant|32
Chicago Bulls|Steve Kerr|16
LA Lakers|Paul Gasol|20
LA Lakers|Shaquile ONeal|19
Chicago Bulls|Scottie Pippen|23
.
.
.

【问题讨论】:

  • 使用数据库,它们是为此类任务而设计的。 :)
  • 实际上我是从 MySQL 数据库中获取这些数据的。但 DBA 反驳说最好在 App 层完成。在 MySQL 中用于对团队中的每个球员进行排名并获得每个团队的前 3 名得分手的 SQL 具有子查询、排序依据、拥有......等等,DBA 正在推动这些。
  • 考虑更换 DBA。这是半个笑话,但说真的,我不希望 DBA 会给出这样的答案。
  • 数据似乎已经按分数(第 3 列)排序,是这样吗?如果是这样,您只需要第一列的稳定排序:sort -s -t\| -k1 file.in
  • 根据this page on dev.mysql.com 很简单:SELECT team, name, score FROM basketball ORDER BY team, score DESC;。如果这足以导致您的数据库明显变慢,则说明它有问题。

标签: perl sorting ksh


【解决方案1】:

你不需要排序。

 #!/usr/bin/perl
use warnings; use strict;
my %high_score;

while (<DATA>) {
    chomp;
    my ($team_name, $player, $score) = split(/\|/);
    for ($high_score{$team_name}{$player}) {
        $_ = $score
            unless $_ && $_ > $score
    }
}

for my $team_name (sort keys %high_score) {
    my %team_scores = %{ $high_score{$team_name} };
    my @top_players = sort { $team_scores{$b} <=>  $team_scores{$a} } (keys %team_scores);

    my $n = 0;
    for my $player (@top_players) {
        print "$team_name, $player high score: $team_scores{$player}\n";
        last if ++$n >= 2;
    }
}

__DATA__
Chicago Bulls|Michael Jordan|38
Chicago Bulls|Scottie Pippen|23
Chicago Bulls|Poor Joe|10
Chicago Bulls|Steve Kerr|16
LA Lakers|Kobe Bryant|32
LA Lakers|Paul Gasol|20
LA Lakers|Shaquile ONeal|19

编辑:(1) 更新要求 (2) s/while/for/

【讨论】:

  • 非常感谢。最初的意图是从每支球队中挑选出前 2 名(或任意数量)的球员并将其写入文件。不是从每支球队中找到最高分+球员。
  • 谢谢!!你能用 $_ = $score 解释你在做什么,除非 $_ && $_ > $score。
  • 这个语句有效地找到了最大值。它在大于 $score{$team_name}{$player} 时保存$score。请注意,$_ 是 $high_score{$team_name}{$player} 的别名,因此分配给 $_ 也会更新哈希条目。
  • 那么,如果我有一个包含 20M 行的文件,将它们全部放入哈希中不会占用所有(或大量)内存?
  • +1 FWIW,你不需要那个 %team_scores 副本——你可以直接keys %{$HoH-&gt;{foo}}。 (嗯,将 $high_score{$team} hashrefs 重新键入为 ...-&gt;{score}-&gt;{player} 也会更节省内存,因为您最多只需要 N 个 score 键)另外,一个数组切片(@987654326 @) 将比 last if ++$n &gt;= $limit 逻辑更具可读性/惯用恕我直言。
【解决方案2】:

我认为您不能告诉sort 在一列中升序排序,在另一列中降序排序。但是,您可以使用-s 选项在管道中使用两个sorts 以实现稳定排序:

sort -t\| -rnk3 file.in | sort -st\| -k1

【讨论】:

  • 谢谢!!这很有帮助。
  • 如果数字是浮点数...比如说 0.02, 0.5, 20.5 如果我们使用 -n 选项,它会不会正确排序?我试过了,不行。
  • 它也适用于浮点数。我需要提供 -k3,1 和 -k1,1 才能正常工作。
  • 排序-t\| -rnk3,1 file.in.unsorted |排序-st\| -k1,1 > file.in.sorted
【解决方案3】:

我不知道sort 会不会被这么大的文件破坏,但是你可以试试下面的命令。它用管道分隔字段,然后按第一个字段和第三个字段按数字倒序排序,(-r),后代:

sort -t'|' -k1,1 -k3,3nr infile

它产生:

Chicago Bulls|Michael Jordan|38
Chicago Bulls|Scottie Pippen|23
Chicago Bulls|Steve Kerr|16
LA Lakers|Kobe Bryant|32
LA Lakers|Paul Gasol|20
LA Lakers|Shaquile ONeal|19

【讨论】:

  • 我过去曾对这样的大型输入使用过排序,它通常非常有效
  • 我可以在 field1 上按升序排序,在 field3 上降序排序吗? -r 将导致两个字段的反向排序...正确吗?
  • @Birei 您的建议产生了以下结果。
  • 排序-t'|' -k1,1 -nk3,1 player.in
  • 芝加哥公牛队|史蒂夫·科尔|16
    洛杉矶湖人队|沙奎尔·奥尼尔|19 洛杉矶湖人队|保罗·加索尔|20 芝加哥公牛队|斯科蒂·皮蓬|23 洛杉矶湖人队|科比·布莱恩特|32 芝加哥公牛队|迈克尔约旦|38
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-22
  • 2017-04-04
相关资源
最近更新 更多