0

回来征求你的意见。我编写了一个 perl 脚本,该脚本将特定数字的命中次数计入用户定义的 bin 中。例如,这是我的数据文件:

12
14
15
20
21

我想知道我在以下范围内有多少点击:

1-19
20-29
30-39

所以结果就像

1-19    3
20-29   2
30-39   0

我做了这样的事情,首先将我的数据保存到哈希(datahash)中,然后将我的范围保存到另一个哈希(rangehash)中,然后基本上遍历datahash中的所有数据点并检查值是否在范围内范围哈希。

问题在于,对于 datahash 中的每个数据点,我循环遍历所有 rangehash 值并在找到数据点所在的范围后退出。这对少数数据点很有用,但现在我的文件至少有 200 万个数据点和 50,000 个范围,所以循环遍历所有这些只需要很长时间。

我想知道是否有人会有更好的解决方案,而不仅仅是遍历整个事情。其他语言的建议很受欢迎!!!

最好的,

萨克蒂

4

3 回答 3

5

以下将是超快的,尽管它假设不会出现零:

my @buckets = (0) x 4;
++$buckets[ $_ / 10 ] while <>:
print " 1-19: ".( $buckets[0] + $buckets[1] )."\n";
print "20-29: $buckets[2]\n";
print "30-39: $buckets[3]\n";

以下更通用的解决方案实际上可能更快:

use List::Util qw( sum );
++$counts[$_] while <>:
print " 1-19: ".( sum 0, @counts[ 1..19] )."\n";
print "20-29: ".( sum 0, @counts[20..29] )."\n";
print "30-39: ".( sum 0, @counts[30..39] )."\n";
于 2013-08-08T00:36:20.993 回答
3

最简单的方法可能是将数字与哈希相加,然后将相应范围的哈希切片相加。您也可以使用数组而不是哈希,因为您可以使用数字作为索引。这可能会创建非常大的空散列,这很浪费,但它简化了密钥生成,因为错误的索引会发出警告。

use strict;
use warnings;
use List::Util 'sum';

my %nums;
while (<DATA>) {
    s/\D+//g;     # remove junk
    $nums{$_}++;  # count number
}
my $low = 1;
for my $high (qw(19 29 39)) {
    my $sum =  sum(0,                      # to avoid undef return value
                   grep defined,           # avoid uninitialized warnings
                   @nums{$low .. $high});  # hash slice for our range
    print "$low - $high : $sum\n";
    $low = $high + 1;                      # set new low range
}

__DATA__
12
14
15
20
21

输出:

1 - 19 : 3
20 - 29 : 2
30 - 39 : 0
于 2013-08-07T22:17:41.670 回答
0

这仅适用于用户定义的分箱,即不能轻易计算的分箱int($x / 100)*100等。

最近这里或 perlmonks 上有一个类似的问题(我不容易找到),而 IMO 的最佳答案是“排序箱的上限,然后使用二进制搜索”。

对于 50K 箱,这大约if是每个数据点 16 秒,这可能是可以的(当然不是“永远”)。

根据数据,可能会应用一些缓存来实现进一步的加速。例如,可以将数据四舍五入到预期间隔的 1/1000(最后一个 bin - 第一个 bin),并且只检查覆盖这部分的 bin。(我只是编造了这个,但它可能会起作用。或者不会。)。

于 2013-08-08T09:05:40.903 回答