【问题标题】:Most efficient way to make a Histogram from an array in Java从 Java 中的数组制作直方图的最有效方法
【发布时间】:2015-09-01 23:29:19
【问题描述】:

我想通过分箱来计算双精度数组中数字的出现频率(下面的示例数组)。与 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;
}

【问题讨论】:

    标签: java algorithm histogram


    【解决方案1】:

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

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

    如果您的数据表明可能的值是一小组数字,请考虑类似一次通过的方法,然后即时进行计数。这样您就不需要进行排序步骤了。

    附注:当数据量很大时,我的经验主要也与空间复杂性有关,想想一台 RAM 有限但硬盘很大的机器。那样的话,我会考虑瓶颈在硬盘读写上,或者在分布式系统上可以在网络存储上。像你的新 double[binEdges.length - 1] 这样的东西可能会导致 OutOfMemory。

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

    【讨论】:

      【解决方案2】:

      您可以使用 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;
      }
      

      【讨论】:

      • 感谢 TreeMap 是一个不错的选择,让我更新代码,看看这会带来什么时间进度。我在想,如果我们进行 n 次二进制搜索,时间不应该接近 O(nlgn)。
      • @ZahaibAkhtar 我认为O(n * log k) 其中n 是数据大小,k 是binEdges 大小。
      • 同意,它将根据 binEdges 的大小进行缩放,如果 binEdges 与数据大小相同,则在最坏的情况下会趋于 n。
      【解决方案3】:

      @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();
      }
      

      【讨论】:

        猜你喜欢
        • 2015-09-30
        • 1970-01-01
        • 2013-01-21
        • 1970-01-01
        • 2011-12-09
        • 1970-01-01
        • 1970-01-01
        • 2011-03-06
        • 2013-05-18
        相关资源
        最近更新 更多