在d3.js v4中绘制滚动/移动平均线

swy*_*wyx 2 javascript d3.js

这是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,则返回当前曲线工厂,默认为curveLinear.

我非常迷失,那里没有好的例子.任何人都可以为d3 v4 更新上述问题(在d3.js中绘制滚动/移动平均线)?

非常感谢.我已经浪费了太多时间在这上面了:(

Mar*_*ark 6

另一个答案非常好,可能是实现您目标的最佳方式。但为了后人的缘故,这里有一个新的 d3 形状曲线工厂方法中的 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>
Run Code Online (Sandbox Code Playgroud)


kbt*_*tzr 5

他们实际上正在使用d3.v2,所以他们的选择当时可能有限.但是在d3.v4上我没有看到自定义曲线工厂的需要,只需计算移动平均线并用a绘制它就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)
}
Run Code Online (Sandbox Code Playgroud)
path {
    stroke: black;
    fill: none;
}
.avg {
    stroke: #ff0000;    
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://d3js.org/d3.v4.min.js"></script>
Run Code Online (Sandbox Code Playgroud)