数据图表趋势线算法——最小二乘法,这是你的Echarts趋势线

众所周知,Echarts是百度的图表框架,用于做统计图表是比较得心应手的工具,但是所谓百密而有一疏,Echarts居然没有描绘趋势线功能,用过Excel的小伙伴都知道,Excel统计图表可以自动生成趋势线,什么线性的啊,曲线的啊…什么什么数学术语性的啊…

之前,趋势线功能只是在需求之外想了想,也没有搞出来,就给忘了,后来,有个小伙伴又提到了,还提到趋势线可能应该是算出来的,顿时恍然大悟,找到学数学的小伙伴问了问,原来,这个趋势线的专业术语叫做“拟合曲线”,描述这种曲线的算法可以叫做拟合算法,那就好搞了,直接百度拟合算法,最终找到一个叫“最小二乘法”的算法,可以求出趋势线!那么下面就说说怎么计算吧(还好数学没有全部还给Teachers,不然公式都看不懂…)

假设我们有一组n个数据的列表需要统计,如:[y1, y2, y3, … yn],这时,我们假设每个数据的x都是间隔单位1,即[x1=1, x2=2, x3=3, … xn=n],这样就可以将该数据描述在一个xy坐标系中了,而最小二乘法可以通过上述的数据算出一个线性方程式:y = bx  + a,这个方程式就是描述上述n个数据的线性趋势线的公式,下面是求a和b的二元一次方程式:

∑(y) = b∑(x) + na
∑(xy) = b∑(xx) + a∑(x)

其中,
∑(y) – n个数据之和,
∑(x) – n个数据对应的x之和,即1~n之和
∑(xx) – 对应的x平方之和
∑(xy) – 对应的x乘y之和

通过上述方程得到a和b的代数式:

b = ( n∑(xy) – ∑(x)∑(y) ) / ( n∑(xx) – ∑(x)∑(x) )
a = ( ∑(y) – b∑(x) ) / n

到这里,公式就算完了,那么就是java代码了(其实算法是通用的,java和js都可以,php,c什么的也没问题)

/**
 * 根据最小二乘法计算趋势线公式 y=bx+a 的a,b值,
 * 并根据算出来的a,b值计算出每个x对应的y值,
 * 即为趋势线的y值,返回y值数组,直接用于echarts
 *
 * @param
 * 		values - n个数据的数组
 * @return
 * 		ys - 计算出的n个数据的趋势线数据数组
 */
private double[] calTrendline(double[] values) {
	/*
	 * 根据最小二乘法求解,需要以下参数
	 * ∑x, ∑y, ∑xx, ∑xy
	 * 此处x取单位1即可,依次++
	 */
	int n = values.length;
	int sumX = 0;
	double sumY = 0.0;
	int sumXX = 0;
	double sumXY = 0.0;
	for (int i = 1; i <= n; i ++) {
		sumX += i;
		sumY += values[i - 1];
		sumXX += (i * i);
		sumXY += (i * values[i - 1]);
	}

	// 求a,b
	double b = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
	double a = (sumY - b * sumX) / n;
	System.out.println(a + "," + b);

	// 返回趋势线y值
	double[] ys = new double[n];
	for (int i = 1; i <= n; i ++) {
		// 保留3位小数,绝对够准确了
		BigDecimal bd = new BigDecimal( b * i + a );
		ys[i - 1] = bd.setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	return ys;
}

这样就可以将上述代码返回的double数组用于echarts了,当然java处理好了,会将数据保存到request,然后传给jsp,再生成带echarts的js的页面,那么echarts的option部分如下:

var option =
	{
	    title : {
	        text: 'Echarts趋势线案例',
	        subtext: '————最小二乘法'
	    },
	    tooltip : {
	        trigger: 'axis'
	    },
	    legend: {
	        data:['数据','趋势线']
	    },
	    toolbox: {
	        show : true
	    },
	    xAxis : [
	        {
	            type : 'category',
	            data : ['x1','x2','x3','x4','x5','x6','x7','x8','x9','x11','x...n'],
	            axisLabel: {
	        		interval: 0
	        	}
	        }
	    ],
	    yAxis : [
	        {
	            type : 'value'
	        }
	    ],
	    series : [
	        {
	            name:'数据',
	            type:'bar',
	            data: ${values} // 通过controller保存在request中的值
	        },
	        {
	            name:'趋势线',
	            type:'line',
	            data: ${ys} // 通过controller保存在request中的值
	        }
	    ]
	};

做完了这些,那么趋势线就算大功告成了,下面贴一个我生成的趋势线案列吧~

其实趋势线不止线性的,还有各种曲线,这个以后要用了再研究吧,方法应该是一样的,就是方程会不会解的问题了…呵呵…呵…

本文《数据图表趋势线算法——最小二乘法,这是你的Echarts趋势线》来自 www.juwends.com ,欢迎转载或CV操作,但请注明出处,谢谢!