Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Plot rolling/moving average in d3.js v4

this is a necessary update to this question asked for d3 v3.

as mentioned here https://github.com/d3/d3/blob/master/CHANGES.md#shapes-d3-shape

4.0 introduces a new curve API for specifying how line and area shapes interpolate between data points. The line.interpolate and area.interpolate methods have been replaced with line.curve and area.curve. Curves are implemented using the curve interface rather than as a function that returns an SVG path data string; this allows curves to render to either SVG or Canvas. In addition, line.curve and area.curve now take a function which instantiates a curve for a given context, rather than a string.

this is a shocking amount of change and breaks all prior examples unless I'm missing something extremely obvious. of course, the new line.curve documentation https://github.com/d3/d3-shape#line_curve is less than helpful:

line.curve([curve]) <>

If curve is specified, sets the curve factory and returns this line generator. If curve is not specified, returns the current curve factory, which defaults to curveLinear.

I am extremely lost and there are no good examples out there. Can anyone please update the above question (Plot rolling/moving average in d3.js) for d3 v4?

thanks very much. I've wasted too many hours on this already :(

like image 789
swyx Avatar asked Dec 15 '22 01:12

swyx


1 Answers

They're actually using d3.v2 so maybe their options were limited back then. But on d3.v4 I don't see the need of a custom curve factory when simply calculating the moving average and plotting it with a curveBasis gives you the exact same result.

Below you can find a simplified and updated fiddle for the question you mentioned.

movingAvg = function (data, neighbors) {
  return data.map((val, idx, arr) => {
    let start = Math.max(0, idx - neighbors), end = idx + neighbors
    let subset = arr.slice(start, end + 1)
    let sum = subset.reduce((a,b) => a + b)
    return sum / subset.length
  })
}

var data = [3, 66, 2, 76, 5, 20, 1, 30, 50, 9, 80, 5, 7]
var dataAvg = movingAvg(data, 1)

console.log(data.length, data)
console.log(dataAvg.length, dataAvg)

var w = 20,
    h = 80

var x = d3.scaleLinear()
    .domain([0, 1])
    .range([0, w])
    
var y = d3.scaleLinear()
    .domain([0, 100])
    .rangeRound([h, 0])


var straightLine = d3.line()
    .x((d,i) => x(i))
    .y(d => y(d))
    
var curvedLine = d3.line()
    .x((d,i) => x(i))
    .y(d => y(d))
    .curve(d3.curveBasis)

window.onload = function() {
	var chart = d3.select("body").append("svg")
		.attr("class", "chart")
		.attr("width", w * data.length -1)
		.attr("height", h + 200)

	chart
		.append('path')
		.attr('class', 'avg')
		.datum(dataAvg)
		.attr('d', curvedLine)

	chart
		.append('path')
		.datum(data)
		.attr('d', straightLine)
}
path {
    stroke: black;
    fill: none;
}
.avg {
    stroke: #ff0000;    
}
<script src="https://d3js.org/d3.v4.min.js"></script>
like image 182
kbtz Avatar answered Dec 17 '22 01:12

kbtz