Spike detection

by Sébastien Lorentz

HTML

<script type="text/javascript" src="https://static.fusioncharts.com/code/latest/fusioncharts.js"></script>
<script type="text/javascript" src="https://static.fusioncharts.com/code/latest/themes/fusioncharts.theme.fint.js?cacheBust=56"></script>


<div id="results">

</div>

<div id="chart-container" style="height: 400px"></div>

JavaScript

let data = [0, 0, 0, 0, 0, 0.1, 0.2, 0.1, 0, -0.2, 0.9, 10, 25, 20, 9, 2, 1, 0, -0.5, 1.1, 0.2, 0, 0, 0, 0, 0, -5, -7, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const WINDOW_SIZE = 7;
const FLAT_THRESHOLD = 2.5;
const RATIO_THRESHOLD = 3;

function average(data = []) {
  return data.reduceRight((a, b) => a + b) / data.length;
}

function median(data = []) {
  return (Math.min(...data) + Math.max(...data)) / 2;
}

function getSpikes() {
  document.getElementById('results').innerHTML = "";
  for (let i = 0; i < data.length - WINDOW_SIZE; i++) {
    const window = data.slice(i, i + WINDOW_SIZE);

    // If start and end relatively close, we have a window to test
    let windowTestable = (Math.abs(data[i] - data[i + WINDOW_SIZE]) < FLAT_THRESHOLD);

    let ratio = (Math.round((average(window) - median(window) * 100)) / 100);
    let spikeInWindow = Math.abs(ratio) > RATIO_THRESHOLD;
    let direction = Math.sign(0 - ratio); // Ratio is negative is spike is upward

    let str = i + " => ";
    if (windowTestable) {
      str += 'Window is <b>testable</b>.';
      if (spikeInWindow) {
        str += ` There is <b>a spike</b>. Direction is <b>${direction > 0 ? 'upwards' : 'downward'}</b>.`;
      } else {
        str += ' There is <b>no spike</b>.';
      }
    } else {
      str += 'Window is <b>not testable</b>: start and end values of window are not close.';
    }


    let p = document.createElement('p');
    p.innerHTML = str;
    document.getElementById('results').appendChild(p);

  }
}

getSpikes()

FusionCharts.ready(function() {
  var fusioncharts = new FusionCharts({
    type: 'dragline',
    renderAt: 'chart-container',
    width: '500',
    height: '350',
    dataFormat: 'json',
    dataSource: {
      "chart": {
        "caption": "Coordonnées",
        "xAxisName": "Index",
        "yAxisName": "value",
        "theme": "fint",
      },
      "categories": [{
        "category": data.map((element, index) => {
          return {
            "label": index
         ...