5

如果我有如下所示的数据:

harvest = [{type: "apple", color: "green", value: 1}, 
           {type: "apple", color: "red", value: 2}, 
           {type: "grape", color: "green", value: 3},
           {type: "grape", color: "red", value: 4 }]

我可以使用 d3 的 nest.rollup() 函数通过各种属性对其进行总结:

sum_by = "color";

rollup = d3.nest().key(function(d) {
  return d[sum_by];
}).rollup(function(d) {
  return d3.sum(d, function(g) {
    return g.value;
  });
}).entries(harvest);

给我这个:

rollup = [{key: "green", values: 4},
          {key: "red", values: 6}]

这正是我想要的。

但是,我的数据中的值由长度相同的数组组成:

harvest = [{type: "apple", color: "green", values: [1,2,3,4]}, 
           {type: "apple", color: "red", values: [5,6,7,8]}, 
           {type: "grape", color: "green", values: [9,10,11,12]},
           {type: "grape", color: "red", values: [13,14,15,16] }]

是否可以以类似的方式组合这些?举个例子:

rollup = [{key: "green", values: [10,12,14,16]},
          {key: "red", values: [18,20,22,24]}]

我觉得这可能使用 d3 汇总函数(但不一定必须使用 d3 来完成)。

解析度

感谢@meetamit 和@Superboggly 的努力,我有三个解决方案:

版本 1(首选,因为它只使用reduce()一次且map()只使用一次):

function sumArrays(group) {
  return group.reduce(function(prev, cur, index, arr) {
    return {
      values: prev.values.map(function(d, i) {
        return d + cur.values[i];
      })
    };
  });
}

版本 2:

function sumArrays(group) {
  return group.map(function(h) {
    return h.values;
  }).reduce(function(prev, cur, index, arr) {
    return prev.map(function(d, i) {
      return d + cur[i];
    });
  });
}

版本 3(出于兴趣,因为数组长度可能会有所不同):

function sumArrays(group) {
  return group.reduce(function(prev, cur, index, arr) {
    return prev.map(function(d, i) {
      return d + cur.values[i];
    });
  }, [0, 0, 0, 0]);
}

像这样调用:

function rollupArrays() {
  return d3.nest().key(function(d) {
    return d[sum_by];
  }).rollup(sumArrays).entries(harvest);
}

并转换为 CoffeeScript:

rollupArrays = ->
  d3.nest().key (d) ->
    d[sum_by]
  .rollup(sumArrays).entries(harvest)

sumArrays = (group) ->
  group.reduce (prev, cur, index, arr) ->
    values: prev.values.map (d,i) ->
      d + cur.values[i]

更新

如果函数必须运行,即使只有一个输入行,此方法也不适用。见第二部分

4

3 回答 3

5

一种解决方案使用[].reduce()and [].map()

// eg: sumArrays([ [1,2,3,4], [5,6,7,8] ]);// <- outputs [6, 8, 10, 12]
function sumArrays(arrays) {
  return arrays.reduce(
    function(memo, nums, i) {
      if(i == 0)
        return nums.concat();
      else
        return memo.map(
          function(memoNum, i) {
            return memoNum + nums[i];
          }
        );
    },
    [ ]// Start with empty Array for memo
  );
}

reduce 和 map 在旧 JS 中都不是原生的,所以最好使用一个模块(下划线,或者可能有一个 d3 等价于reduce,但我没见过)。

编辑

在您的代码中使用它:

sum_by = "color";

rollup = d3.nest().key(function(d) {
  return d[sum_by];
}).rollup(function(d) {
  var arraysToSum = d.map(function(g) { return g.values; });
  return sumArrays(arraysToSum)
}).entries(harvest);
于 2012-11-14T20:09:18.907 回答
2

@meetamit 我喜欢你使用 reduce 的想法。

如果您只想使用 d3 来解决这个问题,它还有一个内置的 reduce,您可以将其与 nest 函数一起使用:

var rollup = d3.nest().key(function(d) {
  return d[sum_by];
}).rollup(function(d) {
    var result = d.reduce(function(prev, cur, index, arr) {
        return prev.values.map(function(d,i) { return d + cur.values[i];});
    });

    return result;
}).entries(harvest);

如果你愿意,你可以在这里玩。

于 2012-11-15T07:58:18.173 回答
1

该解决方案利用d3.rollupsd3.transpose的新特性d3.array

var input = [
  { type: "apple", color: "green", values: [1, 2, 3, 4] },
  { type: "apple", color: "red",   values: [5, 6, 7, 8] },
  { type: "grape", color: "green", values: [9, 10, 11, 12] },
  { type: "grape", color: "red",   values: [13, 14, 15, 16] }
];

var rolled_up = d3.rollups(
  input,
  vs => d3.transpose(vs.map(d => d.values)).map(vs => d3.sum(vs)),
  d => d.color
);

var output = rolled_up.map(([color, values]) => ({ key: color, values: values }));

console.log(output);
<script src="https://d3js.org/d3-array.v2.min.js"></script>

这:

  • 用于d3.rollups按颜色分组并减少生成的分组值:
    • 第三个参数是对项目进行分组的维度。
    • 第二个参数 ( vs => d3.transpose(vs.map(d => d.values)).map(vs => d3.sum(vs))) 是归约函数,用于转换分组值。
  • 在对结果合并数组的每个部分求和之前,reduce函数d3.transpose用于压缩分组数组
  • 将汇总生成的元素映射到预期的输出格式。

这是由 产生的中间结果d3.rollups

var input = [
  { type: "apple", color: "green", values: [1, 2, 3, 4] },
  { type: "apple", color: "red",   values: [5, 6, 7, 8] },
  { type: "grape", color: "green", values: [9, 10, 11, 12] },
  { type: "grape", color: "red",   values: [13, 14, 15, 16] }
];

var rolled_up = d3.rollups(
  input,
  vs => d3.transpose(vs.map(d => d.values)).map(vs => d3.sum(vs)),
  d => d.color
);

console.log(rolled_up);
<script src="https://d3js.org/d3-array.v2.min.js"></script>

于 2019-06-16T15:42:11.823 回答