我正在创建直方图算法。我正在关注here.
提供的解决方案我想简单地计算每个值出现的次数。
但是我不能完全正确地使用算法。我的代码是:
var values = [2, 4, 6, 3, 3];
var val_max = 6;
var val_min = 2;
var num_bins = parseInt(val_max - val_min + 1);
console.log('num_bins is ', num_bins);
var bin_width = (val_max-val_min)/num_bins;
console.log('bin_width is ', bin_width);
var to_plot = [];
for (var i = 0; i < num_bins; i++) {
to_plot.push(0);
}
for (var x = 0; x < values.length; x++) {
var bin_idx = parseInt((values[x] - val_min) / bin_width);
to_plot[bin_idx] = to_plot[bin_idx] + 1;
}
console.log('to_plot is ', to_plot);
如果你查看控制台日志,你会看到:
to_plot is [1, 2, 1, 0, 0, NaN]
我希望最后一个索引为“1”。但问题是值接近最大值,
bin_idx
超出范围。我该如何调整才能得到以下结果?
to_plot is [1, 2, 1, 0, 1]
jsfiddle 在这里。
这是我会做的:
const data = [2, 4, 6, 3, 3];
print(histogram(data, 1)); // [1, 2, 1, 0, 1]
print(histogram(data, 2)); // [3, 1, 1]
print(histogram(data, 3)); // [4, 1]
print(histogram(data, 4)); // [4, 1]
print(histogram(data, 5)); // [5]
function histogram(data, size) {
let min = Infinity;
let max = -Infinity;
for (const item of data) {
if (item < min) min = item;
else if (item > max) max = item;
}
const bins = Math.ceil((max - min + 1) / size);
const histogram = new Array(bins).fill(0);
for (const item of data) {
histogram[Math.floor((item - min) / size)]++;
}
return histogram;
}
function print(x) {
console.log(JSON.stringify(x));
}
这也适用于非整数值。
我认为你的
bin_width
是错误的。试试这个计算:
var bin_width = (val_max - val_min) / (num_bins - 1);
这使得
bin_width == 1
让您的其余代码工作。
由于 bin 的数量等于
val_min
和 val_max
之间的整数个数,因此 bin_width
为 1,而不是当前计算的 0.8。你在这里基本上是在计算整数。使用此循环生成直方图:
for (var x = 0; x < values.length; x++) {
to_plot[values[x] - val_min] ++;
}
对于那些对直方图输出而不是实现感兴趣的人,d3-array 库提供了一个
bin()
方法,可用于构建直方图。
以下是在
bin()
之上的薄包装,在 Typescript 中,它添加了一些功能并提供类似于 Python 的 numpy.histogram. 的输出
/**
* Computes histogram of an array of numbers.
*
* @remarks
* Requires library d3-array.
*
* @param arr - The input array of numbers.
* @param nBins - Optional number of bins desired. Auto if not passed.
* @param domain - Optional minimum and maximum bin edge values.
* @param clamp - Whether to clamp values in arr to the domain.
* Only relevant if domain is passed. If clamp is false (default),
* data outside the domain will be lost. If clamp is true, the data will
* be kept in the first or last bin.
* @returns An object with keys hist, containing the count of values in
* each bin, and binEdges, containing the edges of each bin.
*/
export const computeHistogram = (
arr: number[],
nBins?: number | null,
domain?: [number, number],
clamp: boolean = false
) => {
if (domain && clamp) {
const [domainMin, domainMax] = domain;
arr.forEach((val, i, arr) => {
arr[i] = Math.min(Math.max(val, domainMin), domainMax);
});
}
let bins = d3Array.bin();
if (domain) bins = bins.domain(domain);
if (nBins) bins = bins.thresholds(nBins);
const d3Hist = bins(arr);
const hist: number[] = d3Hist.map((item) => item.length);
const binEdges = d3Hist.map((item) => item.x0);
if (d3Hist.length) binEdges.push(d3Hist.at(-1)!.x1);
return { hist, binEdges };
};