admin管理员组文章数量:1415475
I'm new to D3 and trying to do a moving average of previous and next values on my data in order to smooth it out.
Currently, I have it working using the 2 previous values + the current value. It does work but 1) how would I also use the next values, and 2) what if I wanted to use the 15 previous and 15 next values? (it would be crazy to have 30 individual vars for storing all of them)
I'm used to traditional javascript but lost as to how to traverse the data this way in D3. Hope someone can enlighten me, thanks.
See the whole code on bl.ocks:
Or just the data parsing code here:
d3.json("by_date_mod.json", function(error, data) {
// Setup each row of data by formatting the Date for X, and by converting to a number for Y.
data = data.rows;
data.forEach(function(d) {
d.key = parseDate(String(d.key));
d.value = +d.value;
});
x.domain(d3.extent(data, function(d) { return d.key; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
// Setup the moving average calculation.
// Currently is a hacky way of doing it by manually storing and using the previous 3 values for averaging.
// Looking for another way to address previous values so we can make the averaging window much larger (like 15 previous values).
var prevPrevVal = 0;
var prevVal = 0;
var curVal = 0
var movingAverageLine = d3.svg.line()
.x(function(d,i) { return x(d.key); })
.y(function(d,i) {
if (i == 0) {
prevPrevVal = y(d.value);
prevVal = y(d.value);
curVal = y(d.value);
} else if (i == 1) {
prevPrevVal = prevVal;
prevVal = curVal;
curVal = (prevVal + y(d.value)) / 2.0;
} else {
prevPrevVal = prevVal;
prevVal = curVal;
curVal = (prevPrevVal + prevVal + y(d.value)) / 3.0;
}
return curVal;
})
.interpolate("basis");
// Draw the moving average version of the data, as a line.
graph1.append("path")
.attr("class", "average")
.attr("d", movingAverageLine(data));
// Draw the raw data as an area.
graph1.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
// Draw the X-axis of the graph.
graph1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Draw the Y-axis of the graph.
graph1.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value");
});
I'm new to D3 and trying to do a moving average of previous and next values on my data in order to smooth it out.
Currently, I have it working using the 2 previous values + the current value. It does work but 1) how would I also use the next values, and 2) what if I wanted to use the 15 previous and 15 next values? (it would be crazy to have 30 individual vars for storing all of them)
I'm used to traditional javascript but lost as to how to traverse the data this way in D3. Hope someone can enlighten me, thanks.
See the whole code on bl.ocks: http://bl.ocks/jcnesci/7439277
Or just the data parsing code here:
d3.json("by_date_mod.json", function(error, data) {
// Setup each row of data by formatting the Date for X, and by converting to a number for Y.
data = data.rows;
data.forEach(function(d) {
d.key = parseDate(String(d.key));
d.value = +d.value;
});
x.domain(d3.extent(data, function(d) { return d.key; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
// Setup the moving average calculation.
// Currently is a hacky way of doing it by manually storing and using the previous 3 values for averaging.
// Looking for another way to address previous values so we can make the averaging window much larger (like 15 previous values).
var prevPrevVal = 0;
var prevVal = 0;
var curVal = 0
var movingAverageLine = d3.svg.line()
.x(function(d,i) { return x(d.key); })
.y(function(d,i) {
if (i == 0) {
prevPrevVal = y(d.value);
prevVal = y(d.value);
curVal = y(d.value);
} else if (i == 1) {
prevPrevVal = prevVal;
prevVal = curVal;
curVal = (prevVal + y(d.value)) / 2.0;
} else {
prevPrevVal = prevVal;
prevVal = curVal;
curVal = (prevPrevVal + prevVal + y(d.value)) / 3.0;
}
return curVal;
})
.interpolate("basis");
// Draw the moving average version of the data, as a line.
graph1.append("path")
.attr("class", "average")
.attr("d", movingAverageLine(data));
// Draw the raw data as an area.
graph1.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
// Draw the X-axis of the graph.
graph1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Draw the Y-axis of the graph.
graph1.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value");
});
Share
Improve this question
asked Nov 12, 2013 at 21:41
jcnescijcnesci
912 silver badges10 bronze badges
2
- I would calculate the values for the moving average separately and then pass that data to D3. – Lars Kotthoff Commented Nov 12, 2013 at 21:47
- @LarsKotthoff If it es to that, sure. I'd just like to rule-out whether D3 has traversal methods to do this first. Thanks, – jcnesci Commented Nov 12, 2013 at 22:21
2 Answers
Reset to default 5You need a function to calculate the moving average:
var movingWindowAvg = function (arr, step) { // Window size = 2 * step + 1
return arr.map(function (_, idx) {
var wnd = arr.slice(idx - step, idx + step + 1);
var result = d3.sum(wnd) / wnd.length;
// Check for isNaN, the javascript way
result = (result == result) ? result : _;
return result;
});
};
var avgData = movingWindowAvg(avg, 7); // 15 step moving window.
Note that this functions fudges the values a bit at the borders of the original array, when a plete window cannot be extracted.
Update: If the result is NaN
, convert the result to the present number in the beginning. Checking result == result
is the remended way of testing for NaN
s in Javascript.
If you really don't need a variable size window, this cumulative average might be a faster option without the slicing overhead:
function cumAvg(objects, accessor) {
return objects.reduce(
function(avgs, currObj, i) {
if (i == 1) {
return [ accessor(currObj) ];
} else {
var lastAvg = avgs[i - 2]; // reduce idxs are 1-based, arrays are 0
avgs.push( lastAvg + ( (accessor(currObj) - lastAvg) / i) );
return avgs;
}
}
}
本文标签: javascriptd3js moving average with previous and next data valuesStack Overflow
版权声明:本文标题:javascript - d3.js moving average with previous and next data values - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745191405a2646926.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论