从Java中的数组制作直方图的最有效方法

时间:2015-06-16 22:48:03

标签: java algorithm histogram

我想通过分箱计算双数组中数字出现的频率(下面的示例数组)。基本上与Python numpy's histogram()提供的功能相同。我在一个受限制的环境中可以访问基本的Java Mathjblas库,但是没有其他内容,也没有其他第三方库,如colt可以安装。

double[] x1 = {1, 1, 2, 2, 1, 3, 2}

我有一个单独的排序数组,它标记binEdges的开头和结尾,如下所示:

binEdges = [4.9E-324, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 4.0, 4.0, 5.0, 5.0, 7.0, 1.7976931348623157E308]

请注意,binEdges数组可能包含重复的元素,我想保留它们。因此,对于给定的binEdges数组,频率计数的结果将如下所示:

binCounts = [0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 3.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]

binCounts数组与binEdges结合使用,从左到右阅读如下,请注意bin区间的大括号:

Bin interval frequency [4.9E-324, 1.0) 0 [1.0, 1.0) 0 [1.0, 1.0) 0 [1.0, 2.0) 3 (since we have 3 ones in x1) . . . . . .

我目前有以下实现,它在O(nlgn)中运行,假设排序需要O(nlgn)。我想知道这是否可以减少到低于O(nlgn)的水平。我也浏览过jblas并且不知道用于分箱的库函数,如果这里的人们对其他本机Java技巧或他们可以指出的聪明索引方案有任何其他见解。关于改进代码以减少运行时间的其他建议也是受欢迎的。

缩短时间非常重要,因为手头的数据非常庞大。

public static double [] binCounts(double[] x, double[] binEdges){
    double [] ret = new double[binEdges.length - 1];
    Arrays.sort(x); // takes O(nlgn), the loop below is effectively O(n)
    int k = 0;
    for (int i = 0; i < binEdges.length - 1; i++) {    
        if (binEdges[i] == binEdges[i+1])
            continue;
        for (int j = k; j < x.length; j++){
            if (x[j] >= binEdges[i+1])
                break;
            else if (x[j] >= binEdges[i] && x[j] < binEdges[i+1]){
                ret[i] += 1;
                k++;
            }
        }
    }
    return ret;
}

3 个答案:

答案 0 :(得分:1)

您可以使用TreeMap二进制搜索binEdges:

public static double[] binCounts(double[] x, double[] binEdges) {
    int binEdgesSize = binEdges.length;
    NavigableMap<Double, Integer> binEdgesMap = new TreeMap<>();
    for (int i = 0; i < binEdgesSize; ++i)
        binEdgesMap.put(binEdges[i], i);
    double [] ret = new double[binEdgesSize - 1];
    for (double d : x) {
        Entry<Double, Integer> e = binEdgesMap.ceilingEntry(d);
        if (e != null)
            ++ret[e.getValue()];
    }
    return ret;
}

答案 1 :(得分:0)

如果你看看你的数据,你可以尝试识别他们是否有任何模式,你可以找出任何最佳案例排序算法可以适应,或者了解图像压缩的方式。

在考虑视频游戏对象时,每个帧更新的协调更新可能只是一点调整,因此我们可以简单地应用冒泡排序,而且大多数情况下它是时间复杂度的最佳情况。

如果您有可能值是一小组数字的数据,请考虑像一次通过,并在运行中进行计数。因此,您真的不需要进行分类步骤。

旁注:我的数据大小很大的经验也主要与空间复杂性有关,想想一台内存有限但硬盘很大的机器。在这种情况下,我会考虑瓶颈是在硬盘上读写,还是在分布式系统中可以在网络存储上。像你的新double [binEdges.length - 1]之类的东西可能会导致OutOfMemory。

此外,尝试使用HashSet或类似的。

答案 2 :(得分:0)

@ saka1029感谢您展示NavigableMap容器类(我不知道)。似乎可以通过消除ret对象并直接使用键来简化此操作。由于binCount映射的值是一个整数,我们可以对其进行递增:

public static double[] binCounts(double[] x, double[] binEdges) {
    int binEdgesSize = binEdges.length;
    // binCount: Key = lower edge of bin; Value = item count
    NavigableMap<Double, Integer> binCount = new TreeMap<>();
    for (int i = 0; i < binEdgesSize; ++i)
        binCount.put(binEdges[i], 0);  // Initialize count to zero
    for (double item : x) {
        Double edge = binCount.floorKey(item);
        if (edge != null)
            binCount.get(edge)++;
    }
    return binCount.values();
}