我想通过分箱计算双数组中数字出现的频率(下面的示例数组)。基本上与Python numpy's histogram()
提供的功能相同。我在一个受限制的环境中可以访问基本的Java Math
和jblas库,但是没有其他内容,也没有其他第三方库,如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;
}
答案 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();
}