我有这样的数据:
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)似乎需要很长时间。有没有办法优化我的代码?
提前感谢任何建议!
答案 0 :(得分:3)
正如其他人所说,你最可能的瓶颈不是哈希或Perl,而是磁盘访问。
将文件拆分为更小的块。 (如果可以的话,使用标准的Unix utils。)
将它们存储在SEPARATE IO源上(理想情况下,不同的控制器上的不同磁盘,最好是在不同的PC上)。
如果你只有几个键(例如每个键大于100-1000行),只需单独运行块,然后将它们全部连接成100x小文件,并整理一个文件。
否则,使用数据库同步处理以存储总和。
答案 1 :(得分:2)
哈希非常有效率。它们可能是解决您问题的最佳方案。但是,根据您的数据,可能会有例外情况:
如果所有键都是(或多或少)连续范围内的整数,那么您可以使用数组,这比散列更有效:
while (<DATA>) {
my ($k, $v) = split;
$array[$k] += $v;
}
for my $i (grep defined $array[$_], 0 .. $#array) {
print "$i $array[$i]\n";
}
如果密钥已经排序,我们不需要任何中间数据结构。只需将总和累加到标量中。当密钥更改时,输出最后一个密钥的总和。
如果您有多个文件,则可以并行应用每个文件的算法并合并结果。这使您的代码以对数时间而不是线性时间运行(也称为大赢)。要么将大文件拆分成较小的块,我们会使用seek
和tell
对文件进行分区。处理器越繁忙,文件汇总的速度就越快。 有一点需要注意:很可能I / O是你的瓶颈。如果必须定期执行此任务,则使用SSD(而不是HDD)可能会显着提高性能。
答案 2 :(得分:1)
如果您的数据看起来像是向我们展示的,那么您似乎按键排序,因此根本不需要哈希。
perl -anE'if($k!=$F[0]){say"$k $s"if$.>1;$k=$F[$s=0]}$s+=$F[1]}{say"$k $s"'
会做到这一点。我怀疑它会很慢。