3

我有这样的数据:

1 10
1 30
1 40
1 10
2 20
2 20
2 30
3 50
3 10
3 10
3 10
4 20
4 10

如果第一列的值匹配,我想将所有值相加,结果将是这样的,

1 90
2 70
3 80
4 30

我这里有我的代码,

while (<DATA>) 
{
my ($a, $b) = split;
$hash{$a}  += $b;
}

foreach $a (sort keys %hash) 
{
$b = $hash{$a};
print OUT "$a $b\n";
}

它适用于样本数据(大约 100MB),但处理我的真实数据(大约 100G)似乎需要很长时间。有什么方法可以优化我的代码吗?

提前欣赏任何建议!

4

3 回答 3

3

正如其他人所说,您最可能的瓶颈不是哈希或 Perl,而是磁盘访问。

将文件分成更小的块。(如果可以的话,使用标准的 Unix 工具)。

将它们存储在单独的 IO 源上(不同的磁盘最好在不同的控制器上,最好在不同的 PC 上)。

  • 如果您只有几个键(例如,每个键 >100-1000 行),只需单独运行块,然后将它们全部连接到 100 倍小文件中,并将该文件作为一个整体进行处理。

  • 否则,使用数据库同步处理以存储总和。

于 2013-07-12T20:26:49.250 回答
2

哈希非常有效。它们可能是您问题的最佳解决方案。但是,可能会有例外,具体取决于您的数据:

  • 如果所有键都是(或多或少)连续范围内的整数,那么您可以改用数组,这比散列更有效:

    while (<DATA>) {
      my ($k, $v) = split;
      $array[$k] += $v;
    }
    
    for my $i (grep defined $array[$_], 0 .. $#array) {
      print "$i $array[$i]\n";
    }
    
  • 如果键已经排序,我们不需要任何中间数据结构。只需将总和累加为标量即可。当key改变时,输出最后一个key的和。

  • 如果您有多个文件,您可以将您的算法并行应用于每个文件并组合结果。这让您的代码以对数时间而不是线性时间运行(又名。一个巨大的胜利)。要么将大文件分割成更小的块,我们对文件seek进行tell分区。您拥有的处理器越繁忙,您的文件汇总速度就越快。有一点需要注意: I/O 很可能是您的瓶颈。如果必须定期执行此任务,则使用 SSD(而不是 HDD)可能会大大提高性能。

于 2013-07-12T20:38:23.537 回答
1

如果您的数据看起来像您向我们展示的那样,那么您似乎已经按键对其进行了排序,因此根本不需要散列。

perl -anE'if($k!=$F[0]){say"$k $s"if$.>1;$k=$F[$s=0]}$s+=$F[1]}{say"$k $s"'

会成功的。我怀疑它会很慢。

于 2013-07-12T20:41:31.950 回答