Reputation: 21
I have the following data array:
ID Name Number ---- ------ -------- 1 G 1 1 G 2 1 F 3
I want to do the following conversion to calculate the average, but I don’t know how to do it.
ID Name Number_sum ---- ------ ------------ 1 G 3 1 F 3
after summing calculate the average
ID Number_avg ---- ------------ 1 3
If you do not pre-sum, then the average value is calculated incorrectly:
ID Number_avg ---- ------------ 1 2
I want to calculate the average value for each ID, but with an even field "Name".
Next, I plan to build a graph for each ID. I have a road identifier - 1. This road consists of 2 sections: G and F. Moreover, section G is divided into 2 more small sections, 1 and 2 km each.
If we consider the usual average value, then we get the average value over the maximum section of the value - a sub-section of the road. But I want to make a calculation based on the average value of the road sections.
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js</title>
<meta charset="UTF-8">
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</head>
<body>
<div id ="test"></div>
<script type="text/javascript">
//dc.js
var inlineND = new dc.NumberDisplay("#test");
//data
var array1 = [
{"ID": 1, "Name": "G", "Number": 1},
{"ID": 1, "Name": "G", "Number": 2},
{"ID": 1, "Name": "F", "Number": 3}
];
var make_calc = function() {
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.ID;}),
DimensionGroup = Dimension.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
++p.count;
p.total += v.Number;
return p;
}
function reduceRemove(p, v) {
--p.count;
p.total -= v.Number;
return p;
}
function reduceInitial() {
return {count: 0, total: 0};
}
inlineND
.group(DimensionGroup)
.valueAccessor(function(p) { return p.value.count > 0 ? p.value.total / p.value.count : 0; });
dc.renderAll();
//console.log(DimensionGroup);
};
make_calc();
</script>
</body>
</html>
Upvotes: 1
Views: 184
Reputation: 21
In order to calculate the average, taking into account the "Name" field, it is necessary to consider the unique occurrence of this field in the reduce function. As a result, when calculating the average value, divide the sum of values by the number of unique values in the "Name" field
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js</title>
<meta charset="UTF-8">
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</head>
<body>
<div id ="test"></div>
<script type="text/javascript">
//dc.js
var inlineND = new dc.NumberDisplay("#test");
//data
var array1 = [
{"ID": 1, "Name": "G", "Number": 1},
{"ID": 1, "Name": "G", "Number": 2},
{"ID": 1, "Name": "F", "Number": 3}
];
var make_calc = function() {
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.ID;}),
DimensionGroup = Dimension.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
++p.count;
p.total += v.Number;
if(v.Name in p.Names){
p.Names[v.Name] += 1
}
else{
p.Names[v.Name] = 1;
p.Name_count++;
};
return p;
}
function reduceRemove(p, v) {
--p.count;
p.total -= v.Number;
p.Names[v.Name]--;
if(p.Names[v.Name] === 0){
delete p.Names[v.Name];
p.Name_count--;
};
return p;
}
function reduceInitial() {
return {count: 0, total: 0, Name_count: 0, Names: {}};
}
inlineND
.group(DimensionGroup)
.valueAccessor(function(p) { return p.value.Name_count > 0 ? p.value.total / p.value.Name_count : 0; });
dc.renderAll();
//console.log(DimensionGroup);
};
make_calc();
</script>
</body>
</html>
Upvotes: 1
Reputation: 20150
I'm not sure if I completely understand, but if you want to group by Name, sum, and and then take the average of all groups, you could put your dimension on Name and use regular reduceSum
:
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.Name;}),
DimensionGroup = Dimension.group().reduceSum(d => d.Number);
Then pass a "fake groupAll" which returns all the rows from the group to the number display, and calculate the average in the value accessor:
.group({value: () => DimensionGroup.all()})
.valueAccessor(a => a.length === 0 ? 0 : d3.sum(a, ({value}) => value) / a.length);
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js</title>
<meta charset="UTF-8">
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</head>
<body>
<div id ="test"></div>
<script type="text/javascript">
//dc.js
var inlineND = new dc.NumberDisplay("#test");
//data
var array1 = [
{"ID": 1, "Name": "G", "Number": 1},
{"ID": 1, "Name": "G", "Number": 2},
{"ID": 1, "Name": "F", "Number": 3}
];
var make_calc = function() {
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.Name;}),
DimensionGroup = Dimension.group().reduceSum(d => d.Number);
inlineND
.group({value: () => DimensionGroup.all()})
.valueAccessor(a => a.length === 0 ? 0 : d3.sum(a, ({value}) => value) / a.length);
dc.renderAll();
//console.log(DimensionGroup);
};
make_calc();
</script>
</body>
</html>
Upvotes: 0
Reputation: 5863
I'm not sure, but are you looking for something like this?
const arr = [
{id: 1, name: 'G', number: 1},
{id: 2, name: 'G', number: 2},
{id: 3, name: 'F', number: 3}
]
const res = arr.reduce((acc, e) => {
const idx = acc.findIndex(x => x.name === e.name)
if (idx !== -1) {
acc[idx].number += e.number
} else {
acc.push(e)
}
return acc
}, [])
console.log(res)
Upvotes: 0