Javascript sums arrays using d3.nest ()

If I had data that looked like this:

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

I could sum it over various attributes using the d3 function 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); 

Give me that:

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

Which is exactly what I want.

However, the values ​​in my data consist of arrays of the same length:

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

Is it possible to combine them in a similar way? For example:

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

I feel this is possible using the d3 accumulation function (but this does not have to be done using d3).

RESOLUTION

Thanks to the efforts of @meetamit and @Superboggly, I have three solutions:

Version 1 (preferable because it uses reduce() only once and map() only once):

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

Version 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]; }); }); } 

Version 3 (for fun, because the length of the array can vary):

 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]); } 

Called like this:

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

And converted to 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] 

UPDATE

This method is not suitable if the function should be executed, even with a single line of input. See Part II

+5
source share
3 answers

One solution uses [].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 ); } 

Both abbreviations and the map are not native in old JS, so it is best to use a module (underscore, or maybe d3 is equivalent to reduce , but I have not seen it).

EDIT

Using it in your code:

 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); 
+5
source

@meetamit I like your idea of ​​using shortening.

If you want to solve this simply by using d3, it also has a built-in shortcut that you can use in combination with the socket function:

 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); 

If you want, you can play with him here .

+2
source

This solution uses d3.rollups and d3.transpose , the new d3.array functions:

 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> 

It:

  • Uses d3.rollups to group by color and reduce the resulting grouped values:
    • The third parameter is the dimension by which the elements are grouped.
    • The second parameter ( vs => d3.transpose(vs.map(d => d.values)).map(vs => d3.sum(vs)) ) is a reduction function that converts grouped values.
  • The shrink function uses d3.transpose to archive grouped arrays before adding up each part of the resulting merged array
  • Matches accumulated items to the expected output format.

Here is the intermediate result obtained by 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> 
0
source

Source: https://habr.com/ru/post/1446004/


All Articles