admin管理员组文章数量:1278881
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 by various attributes using d3's nest.rollup() function:
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);
Giving me this:
rollup = [{key: "green", values: 4},
{key: "red", values: 6}]
Which is just what I want.
However the values in my data consist of arrays, all of equal 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 bine these in a similar way? To give for example:
rollup = [{key: "green", values: [10,12,14,16]},
{key: "red", values: [18,20,22,24]}]
I feel this is probably possible using a d3 rollup function (but it doesn't necessarily have to be done using d3).
RESOLUTION
Thanks to the efforts of @meetamit and @Superboggly I have three solutions:
Version 1 (preferred because it uses reduce()
just once and map()
just 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 interest because array length 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 isn't suitable if the function must run, even with one input row. See Part II
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 by various attributes using d3's nest.rollup() function:
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);
Giving me this:
rollup = [{key: "green", values: 4},
{key: "red", values: 6}]
Which is just what I want.
However the values in my data consist of arrays, all of equal 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 bine these in a similar way? To give for example:
rollup = [{key: "green", values: [10,12,14,16]},
{key: "red", values: [18,20,22,24]}]
I feel this is probably possible using a d3 rollup function (but it doesn't necessarily have to be done using d3).
RESOLUTION
Thanks to the efforts of @meetamit and @Superboggly I have three solutions:
Version 1 (preferred because it uses reduce()
just once and map()
just 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 interest because array length 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 isn't suitable if the function must run, even with one input row. See Part II
Share Improve this question edited Mar 4, 2019 at 15:15 Derek Hill asked Nov 14, 2012 at 13:39 Derek HillDerek Hill 6,4545 gold badges58 silver badges79 bronze badges3 Answers
Reset to default 5One 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 reduce and map are not native in old JS, so best use a module (underscore, or maybe there's a d3 equivalent to reduce
, but I haven't 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);
@meetamit I like your idea of using reduce.
If you want to solve this just using d3 it also has a built in reduce which you can use in conjunction with the nest 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 it here.
This solution makes use of d3.rollups
and d3.transpose
, new features of 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/d3-array.v2.min.js"></script>
This:
- Uses
d3.rollups
to group by color and reduce the resulting grouped values:- The 3rd parameter is the dimension on which to group items.
- The 2nd parameter (
vs => d3.transpose(vs.map(d => d.values)).map(vs => d3.sum(vs))
) is the reducing function, transforming grouped values.
- The reducing function makes use of
d3.transpose
to zip the grouped arrays, before summing each part of the resulting merged array - Maps elements produced by the rollup to the expected output format.
Here is the intermediate result produced 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/d3-array.v2.min.js"></script>
本文标签: d3jsJavascript summing arraysusing d3nest()Stack Overflow
版权声明:本文标题:d3.js - Javascript summing arrays, using d3.nest() - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741234510a2362739.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论