|
@@ -141,227 +141,166 @@
|
|
|
}
|
|
|
//正态分布图
|
|
|
function showEcharts1(seriesValuedata){
|
|
|
-
|
|
|
- let listData = [];
|
|
|
- let xAxis = [];
|
|
|
- seriesValuedata.forEach((item, index) => {
|
|
|
- listData.push(item.num)
|
|
|
- });
|
|
|
-
|
|
|
- let objGroup = listData.reduce(function(obj, name) {
|
|
|
- obj[name] = obj[name] ? ++obj[name] : 1;
|
|
|
- return obj;
|
|
|
- }, {});
|
|
|
-
|
|
|
- let max = Math.max(...listData);
|
|
|
- let min = Math.min(...listData);
|
|
|
-
|
|
|
- //x轴最大最小前后范围
|
|
|
- let dataRangeMinOP = 2;
|
|
|
- let dataRangeMaXOP = 2.1;
|
|
|
- //间距 ,X轴的步距
|
|
|
- let dataRangeOP = 0.1;
|
|
|
- //小数点位数.这个要和数据精确到哪一位相匹配
|
|
|
- let Xpiont = 1;
|
|
|
-
|
|
|
- //处理x轴,把数据范围内的x轴求出来,并设置值轴没有的值为空,按顺序对应
|
|
|
- for (let i = min - dataRangeMinOP; i <= max + dataRangeMaXOP; i += dataRangeOP) {
|
|
|
- let str = i.toFixed(Xpiont).toString();
|
|
|
- xAxis.push(str);
|
|
|
- if (objGroup[str] == null) {
|
|
|
- objGroup[str] = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- //结果不对,用下面这个 解决 0.0!=0,1.0!=1的问题
|
|
|
- /* for (let i = min - dataRangeMinOP; i <= max + dataRangeMaXOP; i += dataRangeOP) {
|
|
|
- let str = i.toFixed(Xpiont).toString();
|
|
|
- xAxis.push(str);
|
|
|
- if (objGroup[Number(str)] == null) {
|
|
|
- objGroup[str] = 0;
|
|
|
- }
|
|
|
- }*/
|
|
|
-
|
|
|
- let barYaxis = [];
|
|
|
- Object.keys(objGroup).sort(function(a, b) {
|
|
|
- return parseFloat(a) - parseFloat(b)
|
|
|
- }).map(key => {
|
|
|
- let num = Math.floor((objGroup[key] / listData.length * 100) * 100) / 100;
|
|
|
- barYaxis.push(num)
|
|
|
- })
|
|
|
-
|
|
|
-
|
|
|
- function sum(array) {
|
|
|
- let s = 0;
|
|
|
- array.forEach(function(val, idx, arr) {
|
|
|
- s += Number(val);
|
|
|
- }, 0);
|
|
|
- return s;
|
|
|
- };
|
|
|
-
|
|
|
- //正太曲线计算的基本数据和方法
|
|
|
- let avg = 0;
|
|
|
- let stdev = 0;
|
|
|
- avg = sum(listData) / listData.length;
|
|
|
-
|
|
|
- let sumXY = function(x, y) {
|
|
|
- return Number(x) + Number(y);
|
|
|
- };
|
|
|
- let square = function(x) {
|
|
|
- return Number(x) * Number(x);
|
|
|
- };
|
|
|
- let mean = listData.reduce(sumXY) / listData.length;
|
|
|
- let deviations = listData.map(function(x) {
|
|
|
- return x - mean;
|
|
|
- });
|
|
|
+ //处理图表数据
|
|
|
+ function getChartData(baseData, cityName) {
|
|
|
+ var data = {
|
|
|
+ baseData: [],
|
|
|
+ xAxisData: [],//x轴
|
|
|
+ realData: [],//实际数值
|
|
|
+ stdplotData: []//正态分布数值
|
|
|
+ };
|
|
|
+ var sumObj = {};//保存每个aqi数值的出现次数
|
|
|
+ baseData.forEach(v=>{
|
|
|
+ var aqi = Number(v.num);
|
|
|
+ data.baseData.push(aqi);
|
|
|
+ if(!sumObj[aqi]){
|
|
|
+ sumObj[aqi] = 1;
|
|
|
+ }else{
|
|
|
+ sumObj[aqi]++;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //console.log(sumObj);
|
|
|
+ var stdObj = getBebeQ(data.baseData);
|
|
|
+
|
|
|
+ var max = stdObj.max;
|
|
|
+ for(var i = 0; i <= max; i++){
|
|
|
+ //console.log(sumObj[i]);
|
|
|
+ data.xAxisData.push(i);
|
|
|
+ data.realData.push(sumObj[i] || 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ var yArr = [];
|
|
|
+
|
|
|
+ //console.log(stdObj);
|
|
|
+ var mean = Number(stdObj.avg);//均值
|
|
|
+ var stdev = stdObj.stdDev;//标准差
|
|
|
+ var x = data.xAxisData;
|
|
|
|
|
|
+ for (var j = 0; j < x.length; j++) {
|
|
|
+ var res = fun(x[j], mean, stdev).toFixed(2);
|
|
|
+ yArr.push(res);
|
|
|
|
|
|
- stdev = Math.sqrt(deviations.map(square).reduce(sumXY) / (listData.length - 1));
|
|
|
-
|
|
|
- //计算正太曲线
|
|
|
- function NDjs(array) {
|
|
|
- let NDarr = [];
|
|
|
- for (let i = 0; i < array.length; i++) {
|
|
|
- let ND = (Math.sqrt(2 * Math.PI) * stdev) * Math.pow(Math.E, (-(Math.pow(array[i] - avg, 2) / (2 * Math.pow(stdev, 2)))));
|
|
|
- NDarr.push(ND);
|
|
|
}
|
|
|
- return NDarr
|
|
|
+
|
|
|
+ data.stdplotData = yArr;
|
|
|
+
|
|
|
+ return data;
|
|
|
}
|
|
|
- let lineYaxis = NDjs(xAxis)
|
|
|
-
|
|
|
|
|
|
- //配置项,本身项目是可以动态在页面配置修改这些属性的,贴到这里用了默认值
|
|
|
- let opacityOption = 'off';
|
|
|
- let opacity = 0.5;
|
|
|
- if (opacityOption == 'off') {
|
|
|
- opacity = 0;
|
|
|
- }
|
|
|
- let endPositionOption = 'all';
|
|
|
- let endPositionPercentum = '';
|
|
|
- let endPosition;
|
|
|
- if (endPositionOption == 'all') {
|
|
|
- endPosition = 100;
|
|
|
- } else if (endPositionOption == 'third') {
|
|
|
- endPosition = 29;
|
|
|
- } else {
|
|
|
- endPosition = endPositionPercentum;
|
|
|
+ //求均值和标准差
|
|
|
+ function getBebeQ(numbers, digit = 2) {
|
|
|
+ // 修复js浮点数精度误差问题
|
|
|
+ const formulaCalc = function formulaCalc(formula, digit) {
|
|
|
+ let pow = Math.pow(10, digit);
|
|
|
+ return parseInt(formula * pow, 10) / pow;
|
|
|
+ };
|
|
|
+ let len = numbers.length;
|
|
|
+ let sum = (a, b) => formulaCalc(a + b, digit);
|
|
|
+ let max = Math.max.apply(null, numbers);
|
|
|
+ let min = Math.min.apply(null, numbers);
|
|
|
+ // 平均值
|
|
|
+ let avg = numbers.reduce(sum) / len;
|
|
|
+ // 计算中位数
|
|
|
+ // 将数值从大到小顺序排列好,赋值给新数组用于计算中位数
|
|
|
+ let sequence = [].concat(numbers).sort((a,b) => b-a);
|
|
|
+ let mid = len & 1 === 0 ?
|
|
|
+ (sequence[len/2] + sequence[len/2+1]) / 2 :
|
|
|
+ sequence[(len+1)/2];
|
|
|
+ // 计算标准差
|
|
|
+ // 所有数减去其平均值的平方和,再除以数组个数(或个数减一,即变异数)再把所得值开根号
|
|
|
+ let stdDev = Math.sqrt(numbers.map(n=> (n-avg) * (n-avg)).reduce(sum) / len);
|
|
|
+ return {
|
|
|
+ max,
|
|
|
+ min,
|
|
|
+ avg: avg.toFixed(digit),
|
|
|
+ mid: parseFloat(mid).toFixed(digit),
|
|
|
+ stdDev : stdDev.toFixed(digit)
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- let persents = 'on';
|
|
|
- let format1;
|
|
|
- let format2;
|
|
|
- if (persents == 'on') {
|
|
|
- format1 = '{value} %'
|
|
|
- format2 = '{c} %'
|
|
|
+
|
|
|
+ //正态分布值
|
|
|
+ function fun(x, u, a) {
|
|
|
+ return (1 / Math.sqrt(2 * Math.PI) * a) * Math.exp(-1 * ((x - u) * (x - u)) / (2 * a * a));
|
|
|
}
|
|
|
-
|
|
|
- let data = [];
|
|
|
- let lineDataSet = {
|
|
|
- type: 'line',
|
|
|
- smooth: true,
|
|
|
- yAxisIndex: 1,
|
|
|
- areaStyle: {
|
|
|
- opacity: opacity
|
|
|
- },
|
|
|
- data: lineYaxis,
|
|
|
- name: '正太分布曲线',
|
|
|
- itemStyle: {
|
|
|
- normal: {
|
|
|
- label: {
|
|
|
- formatter: format2,
|
|
|
- show: false, //开启显示
|
|
|
- position: 'top', //在上方显示
|
|
|
- textStyle: { //数值样式
|
|
|
- fontSize: 16
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+
|
|
|
+ function addParam(arr, target) {
|
|
|
+ //是否是等于
|
|
|
+ var flag = false;
|
|
|
+ var target = parseFloat(target);
|
|
|
+ //最小
|
|
|
+ if (target < parseFloat(arr[0])) {
|
|
|
+ arr.unshift(target.toString());
|
|
|
+ return arr;
|
|
|
}
|
|
|
- }
|
|
|
- let barDataSet = {
|
|
|
- type: 'bar',
|
|
|
- smooth: true,
|
|
|
- yAxisIndex: 0,
|
|
|
- areaStyle: {
|
|
|
- opacity: opacity
|
|
|
- },
|
|
|
- data: barYaxis,
|
|
|
- name: '实际分布',
|
|
|
- itemStyle: {
|
|
|
- normal: {
|
|
|
- label: {
|
|
|
- formatter: format2,
|
|
|
- show: false, //开启显示
|
|
|
- position: 'top', //在上方显示
|
|
|
- textStyle: { //数值样式
|
|
|
- fontSize: 16
|
|
|
- }
|
|
|
- }
|
|
|
+
|
|
|
+ //最大
|
|
|
+ if (target > parseFloat(arr[arr.length - 1])) {
|
|
|
+ arr.push(target.toString());
|
|
|
+ return arr;
|
|
|
+ }
|
|
|
+
|
|
|
+ //中间
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ if (parseFloat(arr[i]) > target) {
|
|
|
+ if (arr[i - 1] == target)
|
|
|
+ flag = true;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
+ if (flag) {
|
|
|
+ return arr;
|
|
|
+ } else {
|
|
|
+ arr.splice(i, 0, target.toString());
|
|
|
+ return arr;
|
|
|
+ }
|
|
|
}
|
|
|
- data.push(lineDataSet, barDataSet);
|
|
|
-
|
|
|
+ var data = getChartData(seriesValuedata);
|
|
|
+
|
|
|
let option = {
|
|
|
- type: 'scroll',
|
|
|
- title: {
|
|
|
- text: ''
|
|
|
+ "legend": {},
|
|
|
+ color: ['rgb(50, 255, 200)', '#f00'],
|
|
|
+ //"color": ["#78b4ff", "#f66bc7", "#2bcba7", "#ff8896", "#79c628", "#6c93ee", "#a9abff", "#f7a23f", "#27bae7", "#ff6d9d", "#cb79ff", "#f95b5a", "#ccaf27", "#38b99c", "#93d0ff", "#bd74e0", "#fd77da", "#dea700"],
|
|
|
+ "xAxis": {
|
|
|
+ "name": "",
|
|
|
+ "data": data.xAxisData
|
|
|
},
|
|
|
- dataZoom: [{
|
|
|
- type: 'inside',
|
|
|
- show: false,
|
|
|
- xAxisIndex: [0],
|
|
|
- start: 0,
|
|
|
- end: endPosition,
|
|
|
- borderColor: '#F5A9D0',
|
|
|
- backgroundColor: '#F5A9D0'
|
|
|
+ "yAxis": [{
|
|
|
+ "name": "次数"
|
|
|
+ },{
|
|
|
+ name: '频率'
|
|
|
+ }],
|
|
|
+ "series": [
|
|
|
+ {
|
|
|
+ "name": "实际出现次数",
|
|
|
+ "data": data.realData,
|
|
|
+ "type": "bar",
|
|
|
+ "smooth": true,
|
|
|
+ /*"areaStyle": {
|
|
|
+ "normal": {
|
|
|
+ "color": "rgba(50, 255, 200, 0.5)"
|
|
|
+ }
|
|
|
+ }*/
|
|
|
},
|
|
|
{
|
|
|
- show: false,
|
|
|
- type: 'slider',
|
|
|
- xAxisIndex: [0],
|
|
|
- start: 0,
|
|
|
- end: endPosition
|
|
|
+ "name": "正态分布",
|
|
|
+ yAxisIndex: 1,
|
|
|
+ "data": data.stdplotData,
|
|
|
+ "type": "line",
|
|
|
+ "smooth": true,
|
|
|
+ /*"areaStyle": {
|
|
|
+ "normal": {
|
|
|
+ "color": "rgba(50, 255, 200, 0.5)"
|
|
|
+ }
|
|
|
+ }*/
|
|
|
}
|
|
|
],
|
|
|
- tooltip: {
|
|
|
- trigger: "item",
|
|
|
- axisPointer: {
|
|
|
- type: "shadow"
|
|
|
- },
|
|
|
- formatter:function(params){
|
|
|
- //console.log(params)
|
|
|
- var v = (params.value*1).toFixed(2)
|
|
|
- return `${params.name} ${params.seriesName}:${v}`
|
|
|
- }
|
|
|
- },
|
|
|
- legend: {
|
|
|
- data: ['正太分布曲线', '实际分布']
|
|
|
- },
|
|
|
- xAxis: {
|
|
|
- boundaryGap: false,
|
|
|
- type: 'category',
|
|
|
- data: xAxis
|
|
|
- },
|
|
|
- yAxis: [{
|
|
|
- type: 'value',
|
|
|
- axisLabel: {
|
|
|
- formatter: format1
|
|
|
- }
|
|
|
- }, {
|
|
|
- show: false,
|
|
|
- type: 'value',
|
|
|
- axisLabel: {
|
|
|
- formatter: '{value} %'
|
|
|
- }
|
|
|
- }],
|
|
|
- grid: [{
|
|
|
- left: '8%',
|
|
|
- top: '10%',
|
|
|
- width: '92%',
|
|
|
- height: '75%'
|
|
|
- }],
|
|
|
- series: data
|
|
|
- };
|
|
|
+ "tooltip": {
|
|
|
+ "trigger": "axis"
|
|
|
+ }
|
|
|
+ }
|
|
|
let myChart = echarts.init(echatrs1.value)
|
|
|
myChart.setOption(option);
|
|
|
}
|