Built with blockbuilder.org
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-random.v1.min.js"></script>
<script src="https://d3js.org/d3-scale.v2.min.js"></script>
<script src="https://d3js.org/d3-selection.v1.min.js"></script>
<script src="https://d3js.org/d3-path.v1.min.js"></script>
<script src="https://d3js.org/d3-shape.v1.min.js"></script>
<script src="sma.js"></script>
<script src="stdv.js"></script>
<style>
.outlier { fill: red }
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<script>
const width = 900;
const height = 500;
const frameLength = 10;
const data = Array.from(
{length: 100},
() => Math.floor(d3.randomIrwinHall(1)() * 10));
const movingAverage = sma(data, frameLength);
let movingStDev = data.map(
(d, i) => standardDeviation(data.slice(i - frameLength, i))
);
const xScale = d3.scaleLinear()
.domain([0, data.length])
.range([0, width]);
const yScale = d3.scaleLinear()
.domain([0, Math.max(...data)])
.range([-height/5, height/5]);
var line = d3.line()
.curve(d3.curveCatmullRomOpen)
.x((d,i) => xScale(i))
.y(d => height / 2 + yScale(d))
var incrementor = -1;
var area = d3.area()
.x((d, i) => {
return xScale(i);
})
.y0((d, i)=> {
const point = isNaN(d.y0) ? 0 : d.y0;
return height / 2 + yScale(point);
})
.y1((d, i)=> {
const point = isNaN(d.y1) ? 0 : d.y1;
return height / 2 + yScale(point);
})
// svg setup
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// dots
const svgData = svg.append("g").classed("data", true);
svgData
.selectAll(".dot")
.data(data).enter()
.append('circle')
.classed("dot", true)
.attr("cx", (d,i) => xScale(i))
.attr("cy", d => (height / 2) + yScale(d))
.attr("r", 2)
.classed("outlier", (d,i) => {
const distance =
d < movingAverage[i]
? movingAverage[i] - d
: d - movingAverage[i];
return distance > movingStDev[i]; })
// average line
svg.append("path")
.datum(movingAverage)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", line);
movingStDev = movingStDev.slice(0, movingStDev.length - frameLength)
// standard deviation band
const bandDataOver = movingStDev.map((d,i) => {
d = d || 0;
mv = +movingAverage[i];
return d + mv; });
const bandDataUnder = movingStDev.map((d,i) => {
d = d || 0;
mv = +movingAverage[i];
return mv - d; });
const bandData = bandDataOver.map(
(d,i) => ({
"y0": d,
"y1": bandDataUnder[i]
}));
svg.append("path")
.datum(bandData)
.attr("fill", "steelblue")
.attr("fill-opacity", 0.2)
.attr("stroke", "steelblue")
.attr("stroke-opacity", 0.5)
.attr("d", area);
// text output
svg.append("text")
.text(data.toString())
.attr("y", 50)
.attr("x", 50)
.attr("font-size", 10)
.attr("font-family", "monospace")
</script>
</body>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-random.v1.min.js
https://d3js.org/d3-scale.v2.min.js
https://d3js.org/d3-selection.v1.min.js
https://d3js.org/d3-path.v1.min.js
https://d3js.org/d3-shape.v1.min.js