这是对此question d3 v3的必要更新。
如此处所述https://github.com/d3/d3/blob/master/CHANGES.md#shapes-d3-shape
4.0引入了一个新的曲线API,用于指定线和区域形状在数据点之间进行插值的方式。 line.interpolate和area.interpolate方法已替换为line.curve和area.curve。使用曲线接口而不是作为返回SVG路径数据字符串的函数来实现曲线;这允许曲线渲染到SVG或Canvas。此外,line.curve和area.curve现在采用一个函数来实例化给定上下文的曲线,而不是字符串。
这是一个令人震惊的变化,并打破了所有先前的例子,除非我错过了一些非常明显的事情。当然,新的line.curve文档https://github.com/d3/d3-shape#line_curve不太有帮助:
line.curve([curve])<>
如果指定了曲线,则设置曲线工厂并返回此行 发电机。如果未指定curve,则返回当前曲线 factory,默认为curveLinear。
我非常迷失,那里没有好的例子。任何人都可以为d3 v4更新上述问题(Plot rolling/moving average in d3.js)吗?
非常感谢。我已经浪费了太多时间在这上面了:(答案 0 :(得分:5)
他们实际上正在使用d3.v2,所以他们的选择当时可能有限。但是在d3.v4上,我只是在计算移动平均线并用curveBasis
绘制它时,不会看到自定义曲线工厂的需要。给你完全相同的结果。
您可以在下面找到针对您提到的问题的简化和更新的小提琴。
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>
&#13;
答案 1 :(得分:3)
另一个答案非常好,可能最好的方式来实现您的目标。但是为了后人的缘故,这里是新的d3形 curve factory 方法中的n移动平均值:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
<script>
function NMoveAvg(context, N) {
this._context = context;
this._points = {
x: [],
y: []
};
this._N = N;
}
NMoveAvg.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
this._points.x.push(x);
this._points.y.push(y);
if (this._points.x.length < this._N) return;
var aX = this._points.x.reduce(function(a, b) {
return a + b;
}, 0) / this._N,
aY = this._points.y.reduce(function(a, b) {
return a + b;
}, 0) / this._N;
this._points.x.shift();
this._points.y.shift();
switch (this._point) {
case 0:
this._point = 1;
this._line ? this._context.lineTo(aX, aY) : this._context.moveTo(aX, aY);
break;
case 1:
this._point = 2; // proceed
default:
this._context.lineTo(aX, aY);
break;
}
}
};
var curveNMoveAge = (function custom(N) {
function nMoveAge(context) {
return new NMoveAvg(context, N);
}
nMoveAge.N = function(N) {
return custom(+N);
};
return nMoveAge;
})(0);
</script>
</head>
<body>
<script>
var data = [3, 66, 2, 76, 5, 20, 1, 30, 50, 9, 80, 5, 7];
var w = 500,
h = 500;
var x = d3.scaleLinear()
.domain([0, 12])
.range([0, w]);
var y = d3.scaleLinear()
.domain([0, 100])
.range([0, h]);
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var line = d3.line()
.x(function(d, i) {
return x(i);
})
.y(function(d, i) {
return y(d);
});
svg.append("path")
.datum(data)
.attr("d", line.curve(curveNMoveAge.N(3)))
.style("fill", "none")
.style("stroke", "steelblue");
svg.append("path")
.datum(data)
.attr("d", line.curve(d3.curveLinear))
.style("fill", "none")
.style("stroke", "black");
</script>
</body>
</html>
&#13;