我有一个以下结构的json:
{“1”:35,“2”:12,....}
密钥是一个数字,该值是db中集合中该值的聚合(另一个数字)。 可能有数千个不同的密钥。 我想将此聚合转换为最多10个桶的直方图。 我想使用d3js库,因为我们已经将它用于其他事情。 从api看来,它只知道接受一组数字,但我想自定义值。
我只需要一个对象而不关心可视化。 谢谢你的帮助!
答案 0 :(得分:0)
D3已有直方图功能,如果你通过API,我相信你可以找到它。但是既然你想要一个自定义的解决方案,这些方面的东西都可以工作(我是特意写的,所以检查一下):
让我们创建一个示例数据集:
var x = {};
for (var i =0;i<1000;++i){
x[i]=Math.random()
}
创建一个将接受对象,多个存储桶和存储区间隔的函数:
function allocate(obj,bucketCount,bucketStep){
var objLastIndex = Object.keys(obj).length - 1;
var retValue = Array.apply(null,Array(bucketCount))
.map(function(d,i){
var obj = {};
obj.count = 0;
obj.freq = 0;
return obj;
});
Object.keys(obj).forEach(function(d,i){
var bucketIndex = obj[d]/bucketStep<<0;
retValue[bucketIndex] && ++retValue[bucketIndex].count;
if(i === objLastIndex){
retValue.forEach(function(d,i){
d.freq = d.count/(objLastIndex+1)
})
}
});
return retValue;
}
使用它:
allocate(x,10,0.1);
"[
{
"count": 84,
"freq": 0.084
},
{
"count": 90,
"freq": 0.09
},
{
"count": 113,
"freq": 0.113
},
{
"count": 98,
"freq": 0.098
},
{
"count": 111,
"freq": 0.111
},
{
"count": 108,
"freq": 0.108
},
{
"count": 108,
"freq": 0.108
},
{
"count": 82,
"freq": 0.082
},
{
"count": 108,
"freq": 0.108
},
{
"count": 98,
"freq": 0.098
}
]"
或者:
allocate(x,10,0.5);
"[
{
"count": 496,
"freq": 0.496
},
{
"count": 504,
"freq": 0.504
},
{
"count": 0,
"freq": 0
},
{
"count": 0,
"freq": 0
},
{
"count": 0,
"freq": 0
},
{
"count": 0,
"freq": 0
},
{
"count": 0,
"freq": 0
},
{
"count": 0,
"freq": 0
},
{
"count": 0,
"freq": 0
},
{
"count": 0,
"freq": 0
}
]"
由于您没有提供任何反馈或指示输出,我认为VERSION 1中的格式是您想要的。
现在我将稍微修改上面的功能。我将它分为2,第一部分将返回一个像ES6 Promises或yield关键字返回的对象,另一部分(附在第一部分上,因此它不必一次又一次地创建)将会繁重的举动。如果需要,您可以附加回调:
function allocate(obj,bucketCount,bucketStep,callback){
var keys = Object.keys(obj);
var retValue = Array.apply(null,Array(bucketCount))
.map(function(d,i){
var obj = {};
obj.count = 0;
obj.freq = 0;
return obj;
});
console.log(retValue)
retValue.__parent = {result:retValue,done:false};
allocate._iterate(keys,0,retValue,obj,bucketCount,bucketStep,callback);
return retValue.__parent;
}
allocate._iterate = function(keys,iteration,buckets,obj,bucketCount,bucketStep,callback){
var currentLoad = keys.slice(iteration*10000,++iteration*10000),
currentLoadLength = currentLoad.length,
currentLoadLastIndex = currentLoadLength - 1,
length = keys.length;
currentLoad.forEach(function(d,i){
var bucketIndex = obj[d]/bucketStep<<0;
buckets[bucketIndex] && ++buckets[bucketIndex].count;
});
if(currentLoadLength < 10000) {
buckets.forEach(function(d,i){
d.freq = d.count/length;
});
buckets.__parent.done = true;
return callback && callback(buckets);
} else {
window.requestAnimationFrame(
allocate._iterate(keys,iteration,buckets,obj,bucketCount,bucketStep,callback)
);
}
}
要使用它(最后一个参数是一个可选的回调函数,它将数组作为第一个参数传递):
var x = {}; //1000000 keys, I could do more but chrome could not handle his own object.
for (var i =0;i<1000000;++i){
x[i]=Math.random()
}
var returnValue = allocate(x,10,0.1,function(){console.log("done!")});
//{result: Array(10), done: false}
//after few seconds --> 'done!' is logged
returnValue.result;
//"[
{
"count": 100156,
"freq": 0.100156
},
{
"count": 100575,
"freq": 0.100575
},
{
"count": 100009,
"freq": 0.100009
},
{
"count": 99818,
"freq": 0.099818
},
{
"count": 99785,
"freq": 0.099785
},
{
"count": 100332,
"freq": 0.100332
},
{
"count": 99778,
"freq": 0.099778
},
{
"count": 99790,
"freq": 0.09979
},
{
"count": 99795,
"freq": 0.099795
},
{
"count": 99962,
"freq": 0.099962
}
]"
答案 1 :(得分:0)
看起来ibowenkenobi有一个合理的解决方案,但如果你更愿意利用d3,而不是完全重新发明轮子,你可以。 Here's a JSFiddle
请记住d3.histogram().thresholds()
有点难以预测;你建议一些桶,它会选择一个接近它的实际数字。输入10导致13个桶,在这种情况下。
function parseBaseTenInt(n) {
return parseInt(n, 10);
}
function accumulateValueCounts(accumulator, currentValue) {
return accumulator + data[currentValue.toString()];
}
function calcBinSum(bin) {
return bin.reduce(accumulateValueCounts, 0);
}
var numbers = Object.keys(data).map(parseBaseTenInt);
var histogram = d3.histogram().thresholds(9);
var bins = histogram(numbers);
console.log('bins:', bins);
var binSums = bins.map(calcBinSum);
console.log('totals for each bin:', binSums);