最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to create an average line for an irregular time graph? - Stack Overflow

programmeradmin3浏览0评论

I'm building irregular time graphs with HighCharts that at the moment look like so:

And I'm wondering if it's possible to create an 'average' line for the three (or possibly more in future) lines.

It would start following the blue line, then go closer to the green line mid-January, etc.

At the moment the code I'm working with looks like:

$('#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'
  }]
});

I'm building irregular time graphs with HighCharts that at the moment look like so:

And I'm wondering if it's possible to create an 'average' line for the three (or possibly more in future) lines.

It would start following the blue line, then go closer to the green line mid-January, etc.

At the moment the code I'm working with looks like:

$('#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'
  }]
});
Share Improve this question edited Mar 1, 2014 at 14:42 dachi 1,60211 silver badges15 bronze badges asked Mar 1, 2014 at 14:39 sevenseacatsevenseacat 25k6 gold badges66 silver badges90 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 7

What you are describing is called a trend or regression line. Highcharts doesn't have a built in ability to add these lines, but the math isn't too difficult (and besides, it's more fun to do it yourself). I've coded up the simplest example I can using least squared linear regression.

/////////////////////
//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;
}

The lineFit function will return the rV object (by reference) with attributes of slope and intercept. After that you can add a line to Highcharts with good old fashioned y = slope * x + intercept and minX is the starting value for the regression line and maxX is the ending value.

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

Working fiddle here.

Based on ideas provided by the answer from Mark, I wrote some code to generate a custom fourth line, using the data from all three lines, and calculating the required value for each point.

My new code is as follows:

  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'
    }]
  });

The graph as it looks now (with one extra data point on the blue line) is:

发布评论

评论列表(0)

  1. 暂无评论