【问题标题】:use d3js to convert buckets aggregation to an histogram使用 d3js 将桶聚合转换为直方图
【发布时间】:2023-03-24 11:45:02
【问题描述】:

我有一个如下结构的json:

{"1": 35, "2": 12, ....}

键是一个数字,值是从数据库中的一个集合中聚合的值(另一个数字)。 可能有数千个不同的键。 我想将此聚合转换为最多 10 个桶的直方图。 我想使用 d3js 库,因为我们已经将它用于其他事情。 从 api 看来,它只知道接受一个数字数组,但我想自定义这些值。

我只需要一个对象,并不关心可视化。 感谢您的帮助!

【问题讨论】:

    标签: javascript d3.js histogram


    【解决方案1】:

    看起来 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);
    

    【讨论】:

      【解决方案2】:

      D3 已经有一个直方图函数,如果你通过 API 相信你可以找到它。但是由于您想要一个自定义解决方案,因此可以使用这些方法(我是临时编写的,所以请检查一下):

      版本 1 - 事件循环的单个滴答声 - 同步

      让我们创建一个示例数据集:

      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
          }
      ]"
      

      版本 2 - 多个周期 - 异步 - 处理 >1000000 个点

      由于您没有提供任何反馈或指示输出,我假设 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
          }
      ]"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-06
        • 2021-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多