Browse Source

得分水平分析-正态分布

littleblue55 1 month ago
parent
commit
4cfe61acf4
1 changed files with 211 additions and 73 deletions
  1. 211 73
      src/views/dashboard/sandianscore.vue

+ 211 - 73
src/views/dashboard/sandianscore.vue

@@ -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;