|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
-<!-- 得分水平分布-企业水平分析 - littlegreen - 补充form和label -->
|
|
|
+ <!-- 得分水平分布-企业水平分析 - littlegreen - 补充form和label -->
|
|
|
<div
|
|
|
:class="className"
|
|
|
:style="{ height: '100%', width: width, padding: '20px' }"
|
|
@@ -58,11 +58,9 @@
|
|
|
当前行业: {{ industryMap.get(selectedCode) }}
|
|
|
</div>
|
|
|
<el-form-item>
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- icon="el-icon-search"
|
|
|
- @click="updateChart"
|
|
|
- >搜索</el-button>
|
|
|
+ <el-button type="primary" icon="el-icon-search" @click="updateChart"
|
|
|
+ >搜索</el-button
|
|
|
+ >
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
</div>
|
|
@@ -151,6 +149,14 @@ export default {
|
|
|
energyConsumeScore: "能源消费量得分",
|
|
|
powerConsumeScore: "电力消费量得分",
|
|
|
},
|
|
|
+ keyToLevel: {
|
|
|
+ totalIndustrialValueScore: "totalIndustrialValueLevel",
|
|
|
+ taxableIncomeScore: "taxableIncomeLevel",
|
|
|
+ paidTaxScore: "paidTaxLevel",
|
|
|
+ fundingScore: "fundingLevel",
|
|
|
+ energyConsumeScore: "energyConsumeLevel",
|
|
|
+ powerConsumeScore: "powerConsumeLevel",
|
|
|
+ },
|
|
|
selectedYear: null,
|
|
|
queryParams: {
|
|
|
pageNum: 1,
|
|
@@ -263,9 +269,18 @@ export default {
|
|
|
);
|
|
|
}
|
|
|
const values = filteredData.map((item) => item[this.selectedDataKey]);
|
|
|
+ console.log(filteredData, values, "filteredData")
|
|
|
const minVal = Math.min(...values).toFixed(2);
|
|
|
const maxVal = Math.max(...values).toFixed(2);
|
|
|
- console.log(filteredData, "filteredData");
|
|
|
+ // console.log(minVal, maxVal)
|
|
|
+ // console.log(
|
|
|
+ // filteredData,
|
|
|
+ // "filteredData",
|
|
|
+ // this.selectedDataKey,
|
|
|
+ // values,
|
|
|
+ // "values"
|
|
|
+ // );
|
|
|
+ // console.log(filteredData, "filteredData");
|
|
|
if (
|
|
|
this.selectedRange === null ||
|
|
|
this.selectedRange === "" ||
|
|
@@ -280,35 +295,69 @@ export default {
|
|
|
let maxNum = -1;
|
|
|
// littlegreen - 传入最大值、最小值、间隔,获得区间列表
|
|
|
function generateRangeArray(x1, x2, interval) {
|
|
|
+ // 确保输入为浮点数
|
|
|
x1 = parseFloat(x1);
|
|
|
x2 = parseFloat(x2);
|
|
|
interval = parseFloat(interval);
|
|
|
+
|
|
|
const result = [];
|
|
|
+
|
|
|
+ // 从最接近 x1 的更小的值开始
|
|
|
let current = x1;
|
|
|
+ // console.log(current)
|
|
|
while (current < x2) {
|
|
|
- let next;
|
|
|
- if (current == x1) {
|
|
|
- current = Math.floor(current * 10) / 10;
|
|
|
- }
|
|
|
- next = parseFloat((current + interval).toFixed(2));
|
|
|
- // 保留两位小数
|
|
|
+ let next = parseFloat((current + interval).toFixed(2));
|
|
|
+
|
|
|
+ // 保留两位小数,并添加区间到结果
|
|
|
result.push({
|
|
|
- name: `${current}-${next}`,
|
|
|
+ name: `${current.toFixed(2)}-${next.toFixed(2)}`,
|
|
|
min: current,
|
|
|
max: next,
|
|
|
});
|
|
|
current = next;
|
|
|
}
|
|
|
- // 最后一个区间
|
|
|
- if (current <= x2) {
|
|
|
+
|
|
|
+ // 如果最后一个区间包含最大值,则加入
|
|
|
+ if (current < x2) {
|
|
|
result.push({
|
|
|
- name: `${current}-${x2}`,
|
|
|
+ name: `${current.toFixed(2)}-${x2.toFixed(2)}`,
|
|
|
min: current,
|
|
|
max: x2,
|
|
|
});
|
|
|
}
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
+ // function generateRangeArray(x1, x2, interval) {
|
|
|
+ // x1 = parseFloat(x1);
|
|
|
+ // x2 = parseFloat(x2);
|
|
|
+ // interval = parseFloat(interval);
|
|
|
+ // const result = [];
|
|
|
+ // let current = x1 - (x2 - x1);
|
|
|
+ // while (current < x2) {
|
|
|
+ // let next;
|
|
|
+ // if (current == x1) {
|
|
|
+ // current = Math.floor(current * 10) / 10;
|
|
|
+ // }
|
|
|
+ // next = parseFloat((current + interval).toFixed(2));
|
|
|
+ // // 保留两位小数
|
|
|
+ // result.push({
|
|
|
+ // name: `${current}-${next}`,
|
|
|
+ // min: current,
|
|
|
+ // max: next,
|
|
|
+ // });
|
|
|
+ // current = next;
|
|
|
+ // }
|
|
|
+ // // 最后一个区间
|
|
|
+ // if (current <= x2) {
|
|
|
+ // result.push({
|
|
|
+ // name: `${current}-${x2}`,
|
|
|
+ // min: current,
|
|
|
+ // max: x2,
|
|
|
+ // });
|
|
|
+ // }
|
|
|
+ // return result;
|
|
|
+ // }
|
|
|
// 生成区间标签和频次
|
|
|
const histogramData = [];
|
|
|
let result = generateRangeArray(minVal, maxVal, 0.1);
|
|
@@ -318,29 +367,12 @@ export default {
|
|
|
count += values.filter(
|
|
|
(value) => value >= item.min && value < item.max
|
|
|
).length;
|
|
|
- if (item.max === maxVal) {
|
|
|
+ // console.log(item.max, maxVal)
|
|
|
+ if (item.max == maxVal) {
|
|
|
count += values.filter((value) => value === item.max).length;
|
|
|
}
|
|
|
histogramData.push({ name: item.name, value: count });
|
|
|
});
|
|
|
- // for (let i = 0; i < binCount; i++) {
|
|
|
- // const x0 = minVal + i * Number(this.selectedRange);
|
|
|
- // const x1 = Math.min(x0 + Number(this.selectedRange), maxVal).toFixed(2);
|
|
|
-
|
|
|
- // // const label = `${x0}-${x1}`;
|
|
|
- // // let count = 0;
|
|
|
- // // count += values.filter((value) => value >= x0 && value < x1).length;
|
|
|
- // // if (x1 === maxVal) {
|
|
|
- // // count += values.filter((value) => value === maxVal).length;
|
|
|
- // // }
|
|
|
-
|
|
|
- // // if (count > 0) {
|
|
|
- // // if (count > maxNum) {
|
|
|
- // // maxNum = count;
|
|
|
- // // }
|
|
|
- // // histogramData.push({ name: label, value: count });
|
|
|
- // // }
|
|
|
- // }
|
|
|
|
|
|
const maxHistogramValue = Math.max(
|
|
|
...histogramData.map((item) => item.value)
|
|
@@ -350,6 +382,80 @@ export default {
|
|
|
0
|
|
|
);
|
|
|
|
|
|
+ //--------------------------正态分布----------------------------------
|
|
|
+ // 计算均值
|
|
|
+ const mean = values.reduce((acc, val) => acc + val, 0) / values.length;
|
|
|
+ // console.log(mean.toFixed(2),"mean")
|
|
|
+ // 计算标准差
|
|
|
+ const stdDev = Math.sqrt(
|
|
|
+ values.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0) /
|
|
|
+ values.length
|
|
|
+ );
|
|
|
+
|
|
|
+ // 创建间隔为0.1的区间
|
|
|
+ const center = mean.toFixed(2)
|
|
|
+ const interval = 0.1;
|
|
|
+ let minLimit = Math.floor(Math.min(...values) * 10) / 10; // 向下取整到最近的0.1
|
|
|
+ let maxLimit = Math.ceil(Math.max(...values) * 10) / 10; // 向上取整到最近的0.1
|
|
|
+ console.log("中心", center,minLimit,maxLimit)
|
|
|
+ if(center-minLimit != maxLimit-center){
|
|
|
+ minLimit = Math.floor(minLimit - (maxLimit-center - (center-minLimit)))
|
|
|
+ }
|
|
|
+ const intervals = [];
|
|
|
+ console.log(minLimit,maxLimit, interval)
|
|
|
+ // 乘以1000 是将小数变成整数去计算,小数计算会有误差
|
|
|
+ for (let i = minLimit * 1000 ; i <= maxLimit*1000 ; i += (interval*1000)) {
|
|
|
+ intervals.push(parseFloat((i/1000).toFixed(2)))
|
|
|
+ }
|
|
|
+ // 计算每个间隔的频次
|
|
|
+ const frequency = new Array(intervals.length - 1).fill(0); // 初始化频次数组
|
|
|
+
|
|
|
+ let intervalArr = intervals.slice(0,-1)
|
|
|
+ const intervalStrings = [];
|
|
|
+ for (let i = 0; i < intervalArr.length; i++) {
|
|
|
+ const start = intervalArr[i].toFixed(1); // 保留一位小数
|
|
|
+ const end = ((intervalArr[i]*1000 + interval*1000)/1000).toFixed(1); // 保留一位小数
|
|
|
+ intervalStrings.push({
|
|
|
+ name: `${start}-${end}`,
|
|
|
+ max: end,
|
|
|
+ min: start
|
|
|
+ }); // 构建区间字符串
|
|
|
+ }
|
|
|
+ console.log(intervals,intervalArr)
|
|
|
+ values.forEach((value) => {
|
|
|
+ for (let j = 0; j < intervalStrings.length - 1; j++) {
|
|
|
+ // console.log(value)
|
|
|
+ // 保证包含右边界
|
|
|
+ // console.log(intervals[j].min, intervals[j].max)
|
|
|
+ if (value >= intervalStrings[j].min && value < intervalStrings[j].max) {
|
|
|
+ // console.log(intervals[j],intervals[j + 1])
|
|
|
+ frequency[j]++;
|
|
|
+ break; // 找到对应区间后可以跳出循环
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 确保处理最大值等于maxLimit的情况
|
|
|
+ if (values.some((value) => value == maxLimit)) {
|
|
|
+ frequency[frequency.length - 1]++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算正态分布值
|
|
|
+ const normalDistributionValues = intervals
|
|
|
+ .slice(0, -1)
|
|
|
+ .map((intervalStart, index) => {
|
|
|
+ const intervalEnd = intervals[index + 1];
|
|
|
+ const midpoint = (intervalStart + intervalEnd) / 2; // 每个区间的中点
|
|
|
+ return (
|
|
|
+ (1 / (stdDev * Math.sqrt(2 * Math.PI))) *
|
|
|
+ Math.exp(-0.5 * Math.pow((midpoint - mean) / stdDev, 2))
|
|
|
+ );
|
|
|
+ });
|
|
|
+ // 输出结果
|
|
|
+ // console.log("Intervals:", intervals.slice(0, -1)); // 显示间隔
|
|
|
+ // console.log("Frequencies:", frequency);
|
|
|
+ // console.log("Normal Distribution Values:", normalDistributionValues);
|
|
|
+ //---------------------------------正态分布end---------------------------
|
|
|
// const quadraticData = this.generateQuadraticData(binCount, maxHistogramValue, totalHistogramValue);
|
|
|
// littlegreen - 去掉line和scatter,增加x轴拖动,根据检测区间修改柱状的颜色
|
|
|
const option = {
|
|
@@ -375,7 +481,7 @@ export default {
|
|
|
const xAxisLabel = params[0].axisValueLabel; // x轴的标签
|
|
|
const yAxisLabel = params[0].seriesName;
|
|
|
// 构建自定义的 tooltip 内容,包含年份
|
|
|
- return `${xAxisLabel}<br> ${yAxisLabel}: ${value}`;
|
|
|
+ return `${xAxisLabel}</br> ${yAxisLabel}: ${value} `;
|
|
|
},
|
|
|
},
|
|
|
legend: {
|
|
@@ -383,6 +489,7 @@ export default {
|
|
|
`${this.selectedYear}年 ${
|
|
|
this.keyToChinese[this.selectedDataKey]
|
|
|
}企业数统计`,
|
|
|
+ "正态分布"
|
|
|
],
|
|
|
},
|
|
|
grid: {
|
|
@@ -394,18 +501,24 @@ export default {
|
|
|
xAxis: {
|
|
|
type: "category",
|
|
|
name: "分布区间",
|
|
|
- data: histogramData.map((item) => item.name),
|
|
|
+ // data: histogramData.map((item) => item.name),
|
|
|
+ data: intervalStrings.map(item=>item.name),
|
|
|
axisLabel: {
|
|
|
fontSize: 12,
|
|
|
interval: 0,
|
|
|
rotate: 20,
|
|
|
+ // padding: [0, 0, 0, -200]
|
|
|
},
|
|
|
},
|
|
|
- yAxis: {
|
|
|
+ yAxis: [{
|
|
|
name: "企业数",
|
|
|
type: "value",
|
|
|
minInterval: 1,
|
|
|
- },
|
|
|
+ },{
|
|
|
+ name: '正态分布',
|
|
|
+ type: 'value'
|
|
|
+
|
|
|
+ }],
|
|
|
series: [
|
|
|
{
|
|
|
name: `${this.selectedYear}年 ${
|
|
@@ -419,11 +532,17 @@ export default {
|
|
|
color: function (params) {
|
|
|
const value = params.dataIndex; // 获取当前柱子在数据中的索引
|
|
|
if (flag == 1) {
|
|
|
+ let min = parseFloat(intervalStrings[value].min)*1000
|
|
|
+ let max = parseFloat(intervalStrings[value].max)*1000
|
|
|
+ const detectMin = parseFloat(that.detectMin)*1000
|
|
|
+ const detectMax = parseFloat(that.detectMax)*1000
|
|
|
if (
|
|
|
- result[value].min >= that.detectMin &&
|
|
|
- result[value].max <= that.detectMax
|
|
|
+ min >= detectMin &&
|
|
|
+ max <= detectMax
|
|
|
) {
|
|
|
return "#ffcc00"; // 变更颜色为黄色
|
|
|
+ }else if(min == detectMax){
|
|
|
+ return "#ffcc00";
|
|
|
}
|
|
|
return "rgba(245,0,0,0.6)";
|
|
|
}
|
|
@@ -432,59 +551,78 @@ export default {
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
- data: histogramData.map((item) => item.value),
|
|
|
+ // data: histogramData.map((item) => item.value),
|
|
|
+ data: frequency,
|
|
|
label: {
|
|
|
show: false, // 将show属性设置为false,去掉柱子上的数字
|
|
|
},
|
|
|
},
|
|
|
- // {
|
|
|
- // type: "scatter",
|
|
|
- // symbol: "circle",
|
|
|
- // symbolSize: 10,
|
|
|
- // data: histogramData.map((item, index) => [index, item.value]),
|
|
|
- // itemStyle: {
|
|
|
- // color: "red",
|
|
|
- // },
|
|
|
- // },
|
|
|
- // {
|
|
|
- // type: "line",
|
|
|
- // data: histogramData.map((item, index) => [index, item.value]),
|
|
|
- // lineStyle: {
|
|
|
- // color: "#1a7cc8",
|
|
|
- // },
|
|
|
- // symbol: "none",
|
|
|
- // },
|
|
|
+ {
|
|
|
+ name:"正态分布",
|
|
|
+ yAxisIndex: 1,
|
|
|
+ type: "line",
|
|
|
+ data: normalDistributionValues,
|
|
|
+ lineStyle: {
|
|
|
+ color: "#1a7cc8",
|
|
|
+ },
|
|
|
+ symbol: "none",
|
|
|
+ },
|
|
|
],
|
|
|
};
|
|
|
this.isChartShow = true;
|
|
|
this.chart.setOption(option);
|
|
|
this.chart.on("click", function (param) {
|
|
|
- if (
|
|
|
- typeof param.dataIndex === "number" &&
|
|
|
+ // console.log(param.dataIndex, intervalStrings)
|
|
|
+ if(typeof param.dataIndex === "number" &&
|
|
|
param.dataIndex >= 0 &&
|
|
|
- param.dataIndex < result.length
|
|
|
- ) {
|
|
|
- let dataIndexValue = result[param.dataIndex];
|
|
|
- const filteredArray = filteredData.filter((item) => {
|
|
|
- const isMaxValEqual = result[1] === maxVal;
|
|
|
- const lowerBoundCheck =
|
|
|
- item[that.selectedDataKey] >= dataIndexValue.min;
|
|
|
- const upperBoundCheck = isMaxValEqual
|
|
|
+ param.dataIndex < intervalStrings.length
|
|
|
+ ){
|
|
|
+ let dataIndexValue = intervalStrings[param.dataIndex];
|
|
|
+ const filteredArray = filteredData.filter((item)=>{
|
|
|
+ const isMaxValEqual = parseFloat(dataIndexValue.max) == parseFloat(maxVal);
|
|
|
+ const lowerBoundCheck = item[that.selectedDataKey] >= dataIndexValue.min;
|
|
|
+ const upperBoundCheck = isMaxValEqual
|
|
|
? item[that.selectedDataKey] <= dataIndexValue.max
|
|
|
: item[that.selectedDataKey] < dataIndexValue.max;
|
|
|
-
|
|
|
return lowerBoundCheck && upperBoundCheck;
|
|
|
- });
|
|
|
+ })
|
|
|
+ // console.log(filteredData,filteredArray,that.selectedDataKey)
|
|
|
that.tableData = filteredArray.map((item) => {
|
|
|
return {
|
|
|
industryName: item.code,
|
|
|
enterpriseId: item.enterpriseName,
|
|
|
year: item.year,
|
|
|
value: item[that.selectedDataKey],
|
|
|
- level:"高"
|
|
|
+ level: item[that.keyToLevel[that.selectedDataKey]],
|
|
|
};
|
|
|
});
|
|
|
}
|
|
|
+ // if (
|
|
|
+ // typeof param.dataIndex === "number" &&
|
|
|
+ // param.dataIndex >= 0 &&
|
|
|
+ // param.dataIndex < intervalStrings.length
|
|
|
+ // ) {
|
|
|
+ // let dataIndexValue = intervalStrings[param.dataIndex];
|
|
|
+ // const filteredArray = filteredData.filter((item) => {
|
|
|
+ // const isMaxValEqual = intervalStrings[1] === maxVal;
|
|
|
+ // const lowerBoundCheck =
|
|
|
+ // item[that.selectedDataKey] >= dataIndexValue.min;
|
|
|
+ // const upperBoundCheck = isMaxValEqual
|
|
|
+ // ? item[that.selectedDataKey] <= dataIndexValue.max
|
|
|
+ // : item[that.selectedDataKey] < dataIndexValue.max;
|
|
|
+
|
|
|
+ // return lowerBoundCheck && upperBoundCheck;
|
|
|
+ // });
|
|
|
+ // that.tableData = filteredArray.map((item) => {
|
|
|
+ // return {
|
|
|
+ // industryName: item.code,
|
|
|
+ // enterpriseId: item.enterpriseName,
|
|
|
+ // year: item.year,
|
|
|
+ // value: item[that.selectedDataKey],
|
|
|
+ // level: "高",
|
|
|
+ // };
|
|
|
+ // });
|
|
|
+ // }
|
|
|
// const filteredArray = filteredData.filter((item) => {
|
|
|
// const isMaxValEqual = result[1] === maxVal;
|
|
|
// const lowerBoundCheck = item[that.selectedDataKey] >= dataIndexValue.min;
|