如何为不规则时间图创建平均线?

时间:2014-03-01 14:39:10

标签: javascript jquery highcharts

我正在使用HighCharts构建不规则的时间图,目前看起来像这样:

graph

我想知道是否有可能为三条(或未来可能更多)的线创建一条“平均”线。

它将开始跟随蓝线,然后接近1月中旬的绿线等等。

目前我正在处理的代码如下:

$('#chart').highcharts({
  chart: { type: 'spline' },
  title: { text: '' },
  xAxis: { type: 'datetime' },
  yAxis: {
    title: { text: '' }
  }
  series: [{
    name: 'Line 1',
    data: [
      [Date.UTC(2014,0,16), 173.33],
      [Date.UTC(2014,0,23), 163.33],
      [Date.UTC(2014,0,30), 137.67],
      [Date.UTC(2014,1,6), 176.33],
      [Date.UTC(2014,1,13), 178.67],
      [Date.UTC(2014,1,27), 167.33],
    ],
    color: 'purple'
  },
  {
    name: 'Line 2',
    data: [
      [Date.UTC(2014,0,11), 156.33],
      [Date.UTC(2014,1,15), 167.67],
    ],
    color: 'green'
  },
  {
    name: 'Line 3',
    data: [
      [Date.UTC(2014,0,1), 135],
      [Date.UTC(2014,0,5), 146.33],
      [Date.UTC(2014,0,27), 146.75],
    ],
    color: 'blue'
  }]
});

2 个答案:

答案 0 :(得分:7)

您所描述的内容称为trend or regression line。 Highcharts没有内置的添加这些线条的能力,但数学并不太难(而且,自己做的更有趣)。我用最小二乘的线性回归编写了最简单的例子。

/////////////////////
//utility functions//
////////////////////
// linear regression
// given array of x values and array of y values
// returns rV object with slope/intercept
lineFit = function(xs, ys, rV){
    rV.slope = 0.0;
    rV.intercept = 0.0;
    rV.rSquared = 1.0; // assume perfection

    if (xs.length < 2)
    {
        return false;
    }

    if (xs.Count != ys.Count)
    {
        return false;
    }

    var N = xs.length;
    var sumX = sumFunc(xs,null);
    var sumY = sumFunc(ys,null);
    var funcSq = function(i){return (i*i);}
    var funcSq2 = function(i,j){return (i*j);}
    var sumXx = sumFunc(xs, funcSq);
    var sumYy = sumFunc(ys, funcSq);
    var sumXy = sumFunc(zip(xs,ys),funcSq2); 

    rV.slope = ((N * sumXy) - (sumX * sumY)) / (N * sumXx - (sumX*sumX));
    rV.intercept = (sumY - rV.slope * sumX) / N;
    rV.rSquared = Math.abs((rV.slope * (sumXy - (sumX * sumY) / N)) / (sumYy - ((sumY * sumY) / N)));
    return true;
}   

// sums arrays with optional function transformation
sumFunc = function(arr, func){
    var total = 0;
    $.each(arr, function(i,k){
        if ($.isArray(k)){
            if (func == null){
                k = k[0] + k[1];
            }else{                
                k = func(k[0],k[1]);
            }
        } else {
            if (func != null){
                k = func(k);
            }
        }
        total += k;
    });
    return total;
}

// python style zip function
// to pair to array together
zip = function(arr1,arr2) {
    var rV = [];
    for(var i=0; i<arr1.length; i++){
       rV.push([arr1[i],arr2[i]]);
    }
    return rV;
}

lineFit函数将返回具有斜率和截距属性的rV对象(通过引用)。之后,您可以使用旧式y = slope * x + intercept向Highcharts添加一行,minX是回归线的起始值,maxX是结束值。

 {
    name: 'Regression Line',
    data: [[minX, reg.slope * minX + reg.intercept], 
           [maxX, reg.slope * maxX + reg.intercept]],
    color: 'red',
    marker:{enabled:false},
    lineWidth: 5
  }

工作fiddle here

enter image description here

答案 1 :(得分:2)

根据Mark的回答提供的想法,我编写了一些代码来生成自定义的第四行,使用来自所有三行的数据,并计算每个点所需的值。

我的新代码如下:

  line1 = [
    [Date.UTC(2014,0,16), 173.33],
    [Date.UTC(2014,0,23), 163.33],
    [Date.UTC(2014,0,30), 137.67],
    [Date.UTC(2014,1,6), 176.33],
    [Date.UTC(2014,1,13), 178.67],
    [Date.UTC(2014,1,27), 167.33],
  ];

  line2 = [
    [Date.UTC(2014,0,11), 156.33],
    [Date.UTC(2014,1,15), 167.67],
  ];

  line3 = [
    [Date.UTC(2014,0,1), 135],
    [Date.UTC(2014,0,5), 146.33],
    [Date.UTC(2014,0,27), 146.75],
    [Date.UTC(2014,2,2), 168.75]
  ];

  function average(array, index) {
    sum = array[0][1];
    for(var i = 1; i <= index; i++) {
      sum += array[i][1];
    }

    value = sum / (index + 1);
    return parseFloat(value.toFixed(2));
  }

  // Make a fourth line with all of the data points for the other 
  // three lines, sorted by date    
  all_lines = line1.concat(line2).concat(line3);
  all_lines.sort(function(a, b) { return a[0] - b[0]});

  // Calculate the value for each data point in the fourth line - 
  // the average of all the values before it
  average_line = [];
  for(var i = 0; i < all_lines.length; i++) {
    average_line.push([all_lines[i][0], average(all_lines, i)])
  }

  $('#chart').highcharts({
    chart: { type: 'spline' },
    title: {
        text: '',
    },
    xAxis: {
        type: 'datetime'
    },
    yAxis: {
        title: {
            text: ''
        }
    },
    legend: {
        layout: 'vertical',
        align: 'right',
        verticalAlign: 'middle',
        borderWidth: 0
    },
    series: [{
        name: 'Line 1',
        data: line1,
        color: 'purple'
    },
    {
        name: 'Line 2',
        data: line2,
        color: 'green'
    },
    {
        name: 'Line 3',
        data: line3,
        color: 'blue'
    },
    {
        name: 'Average',
        data: average_line,
        color: 'red'
    }]
  });

现在看起来的图表(蓝线上有一个额外的数据点)是:

enter image description here