spc x-bar 正态分布 echarts demo

news2024/12/23 22:19:30

使用echarts,elementUi,vue编写的spc分析的demo示例.

含x-bar和正态分布图,同一数据可以互转

在这里插入图片描述
在这里插入图片描述

chart.vue

<template>
  <div class="app-container">
    <el-row>
      <el-col :span="4" class="button-container">
        <el-button @click="redrawChart">重新绘制</el-button>
        <el-button type="warning" @click="analyzeData">重新解析数据并绘制</el-button>
        <el-button type="primary" @click="drawNormalChart">正态分布(X)</el-button>
        <el-button type="success" @click="showSpecDialog">更新SPEC(X)</el-button>
        <el-button type="info">计算cpk(X)</el-button>
        <el-button type="danger">危险按钮(X)</el-button>
      </el-col>
      <el-col :span="20">
        <div id="mainChart" style="height: 720px;width: 1080px;"/>
      </el-col>
    </el-row>
    <div>
      min:{{ dData.min.toFixed(3) }}
      max:{{ dData.min.toFixed(3) }}
      mean:{{ dData.min.toFixed(3) }}
      std:{{ dData.min.toFixed(3) }}
    </div>
    <div>
      说明
      1.x-bar等图的上线边界,计算上下边界,如果数据过于集中,自动图表缩放会导致usl甚至是ucl超出y轴范围,不能正常绘制,那么需要手动计算上下边界<br/>
      上边界取max值和usl+0.5*(usl-ucl)更大的那个<br/>
      下边界取min值和lsl-0.5*(lcl-lsl)更小的那个<br/>
      为避免出现小数位数过多/精度不足 取3位小数<br/>
      2.关闭SPEC弹框时由于数据已经被更新,所以在关闭弹框时间上直接绑定了重绘事件.
      但是由于上下边界是在解析数据阶段完成的,所以不会重新解析上下边界
    </div>
    <el-dialog title="更新SPEC" :visible.sync="visibleSpecDialog" width="800px" top="5vh" @closed="onSpecDialogClose" append-to-body>
      <el-form ref="form" :model="dData" label-width="200px">
        <el-form-item label="usl">
          <el-input v-model="dData.usl"/>
        </el-form-item>
        <el-form-item label="ucl">
          <el-input v-model="dData.ucl"/>
        </el-form-item>
        <el-form-item label="lcl">
          <el-input v-model="dData.lcl"/>
        </el-form-item>
        <el-form-item label="lsl">
          <el-input v-model="dData.lsl"/>
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>
</template>

<script>
import * as echarts from "echarts";
import * as utils from './utils';

//定义显示的颜色
let red="#FF0000"
let yellow="#CCCC00"
let green="#009000"

export default {
  data() {
    //参数定义
    return {
      //主chart实例对象
      mainChart: {},
      //从服务器查询数据参数对象
      queryParams: {},
      //从服务器获取的原始数据(originalData)
      oData: {
        usl: 0,
        ucl: 0,
        cl: 0,
        lcl: 0,
        lsl: 0,
        dataList: []
      },
      //预定义维度数组,注意顺序,不要轻易改变,注意需要调整analyzeData的转二维数组的内容
      columns: ["dataTime", "paramValue", "remark"],
      //绘制图需要的数据格式,(drawData)
      dData: {
        //控制参数
        usl: 0,
        ucl: 0,
        cl: 0,
        lcl: 0,
        lsl: 0,
        //计算参数
        min: 0,
        max: 0,
        mean: 0,
        std: 0,
        upperY: 0,
        lowerY: 0,
        //仅提取paramValue作为值数组
        valueData: [],
        //x-barData 数据格式,就是二维数组
        xBarData: [],
        //正态分布数据格式
        normalData: {
          //正态分布前后距离
          dataRangeMinOP: 1,
          dataRangeMaxOP: 1.1,
          //组间距
          interval: 1,
          // 这3个就不构建datasource来处理了,直接用好了
          // 构建离散值x轴
          xAxis: [],
          // 构建柱状y轴
          barYaxis: [],
          // 构建线性y轴
          lineYaxis: []
        }
      },
      // 当前图表类型 1.XR 2.XS 3.WR 4.ZT FIXME
      showChartType: 1,
      //显示更新SPEC弹框标记
      visibleSpecDialog: false
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    /**
      * 初始化方法
     * 调用过程如下:
     * init->queryData->analyzeData->redrawChart->createOption->this.chart.setOption
     */
    init() {
      //从dom加载echarts对象
      let chartDom = document.getElementById("mainChart");
      this.chart = echarts.init(chartDom);
      this.queryData();
    },
    /**
     * 从服务器获取原始数据
     * 实际参数:this.queryParams.... 返回结果到this.oData
     * 回调触发redrawChart
     */
    queryData() {
      this.oData.dataList = []

      //模拟写入数据
      for (let n = 0; n < 100; n++) {
        this.oData.dataList.push({
          dataTime: n + ":00",
          paramValue: 30 + Math.random() * 30,
          remark: (Math.random() * 20).toFixed(3)
        });
      }
      this.oData.usl = 55;
      this.oData.ucl = 50;
      this.oData.cl = 45;
      this.oData.lcl = 40;
      this.oData.lsl = 35;

      //解析原始为图表需要的数据
      this.analyzeData();
    },
    /**
     * 绘制,或重新绘制主chart图
     */
    redrawChart() {
      //清除图表
      this.chart.clear()
      //创建option并绘制
      let option = this.createOption();
      this.chart.setOption(option);
      //弹框说明
      this.$message.success("绘制图表成功!");
    },
    drawNormalChart() {
      if (this.showChartType === 1) {
        this.showChartType = 4;
      } else {
        this.showChartType = 1;
      }
      this.redrawChart();
    },
    showSpecDialog() {
      this.visibleSpecDialog = true;
    },
    //Spec弹框被关闭
    onSpecDialogClose() {
      this.redrawChart();
    },
    /**
     * 将原始数据解析为x-bar等图需要的格式
     * 计算mean,std.,cpk..等数据返回
     * 实际参数:this.oData.... 返回结果到this.dData
     */
    analyzeData() {
      //copy基础数据
      this.dData.usl = this.oData.usl;
      this.dData.ucl = this.oData.ucl;
      this.dData.cl = this.oData.cl;
      this.dData.lcl = this.oData.lcl;
      this.dData.lsl = this.oData.lsl;

      //仅提取paramValue作为值数组
      this.dData.valueData = this.oData.dataList.map(obj => obj.paramValue);
      //计算其他数据,最大最小cpk什么的
      this.dData.min = Math.min(...this.dData.valueData);
      this.dData.max = Math.max(...this.dData.valueData);
      this.dData.mean = this.dData.valueData.reduce((sum, val) => sum + val, 0) / this.dData.valueData.length;
      this.dData.std = utils.getStd(this.dData.valueData, this.dData.mean)

      //计算上下边界,如果数据过于集中,自动图表缩放会导致usl甚至是ucl超出y轴范围,不能正常绘制,那么需要手动计算上下边界
      //取max值和usl+0.5*(usl-ucl)更大的那个
      //取min值和lsl-0.5*(lcl-lsl)更小的那个
      this.dData.upperY = Math.max(this.dData.usl + 0.5 * (this.dData.usl - this.dData.ucl), this.dData.max).toFixed(3)
      this.dData.lowerY = Math.min(this.dData.lsl - 0.5 * (this.dData.lcl - this.dData.lsl), this.dData.min).toFixed(3)

      // 转x-bar需要数据
      // 对象数组转二维数组
      this.dData.xBarData = []
      this.dData.xBarData = this.oData.dataList.map(obj => [obj.dataTime, obj.paramValue, obj.remark]);

      // 转正态分布数据
      // 构建x轴
      let start = this.dData.min - this.dData.normalData.dataRangeMinOP;
      let end = this.dData.max + this.dData.normalData.dataRangeMaxOP;
      // 计算区间数量
      let numIntervals = Math.ceil((end - start) / this.dData.normalData.interval);
      // 构建离散值x轴
      let xAxis = [];
      for (let i = start; i <= end; i = i + this.dData.normalData.interval) {
        let str = i.toFixed(1).toString();
        xAxis.push(str);
      }
      this.dData.normalData.xAxis = xAxis;
      // 构建柱状y轴,遍历数组并计算频数
      let barYaxis = new Array(numIntervals).fill(0);
      this.dData.valueData.forEach((value) => {
        if (value >= start && value <= end) {
          // 找到值所在的区间
          let intervalIndex = Math.floor((value - start) / this.dData.normalData.interval);
          // 增加该区间的频数
          barYaxis[intervalIndex]++;
        }
      });
      this.dData.normalData.barYaxis = barYaxis;
      // 构建线性y轴
      this.dData.normalData.lineYaxis = utils.fxNormalDistribution(xAxis, this.dData.std, this.dData.mean);

      this.redrawChart();
    },
    /**
     * 构建图图表参数
     * @returns
     */
    createOption() {
      //内部匿名方法无法访问到this,用这个处理,下面都采用that
      let that = this;
      //option
      let option = {};
      //正态分布
      if (this.showChartType === 4) {
        //定义实际数据的频数柱状图
        let barDataSet = {
          type: "bar",
          smooth: true,
          yAxisIndex: 0,
          areaStyle: {
            opacity: 0,
          },
          data: that.dData.normalData.barYaxis,
          name: "实际分布频数",
          label: {
            formatter: "{c} %",
            show: false, //默认显示
            position: "top", //在上方显示
            textStyle: {
              //数值样式
              fontSize: 16,
            },
          },
        };
        //计算实际数据的正态分布图
        let lineDataSet = {
          type: "line",
          smooth: true,
          yAxisIndex: 1,
          areaStyle: {
            opacity: 0,
          },
          data: that.dData.normalData.lineYaxis,
          name: "实际正态分布",
          label: {
            formatter: "{c} %",
            show: false, //开启显示
            position: "top", //在上方显示
            textStyle: {
              //数值样式
              fontSize: 16,
            },
          },
        };

        option = {
          title: {
            text: 'SPC',
          },
          //提示框组件
          tooltip: {
            trigger: "axis",
            axisPointer: {
              type: "shadow",
            },
          },
          xAxis: {
            boundaryGap: true,
            type: "category",
            data: that.dData.normalData.xAxis,
          },
          //定义y轴
          yAxis: [{
            type: "value",
          }, {
            type: "value",
          }],
          series: [barDataSet, lineDataSet],
        };
      }
      //x-bar
      else {
        option = {
          title: {
            text: 'SPC',
          },
          tooltip: {
            trigger: 'axis',
            //轴数据指示
            axisPointer: {
              type: 'cross'
            },
          },
          xAxis: {
            type: 'category'
          },
          yAxis: {
            type: 'value',
            max: that.dData.upperY,
            min: that.dData.lowerY
          },
          dataset: [
            {
              //定义数据字段
              dimensions: [{name: "dataTime", type: 'ordinal'}, "paramValue", "remark"],
              //定义数据内容
              source: that.dData.xBarData
            },
          ],
          series: [
            {
              name: 'Dow-Jones index',
              type: 'line',
              //密集点数过多是否显示
              showAllSymbol: true,
              //定义xy轴取数据那一列值
              encode: {
                x: 'dataTime',
                y: 'paramValue',
                tooltip: ["paramValue", "dataTime", "remark"],
              },
              //定义点样式,依据数据定义点样式以及大小颜色
              symbol: function (params) {
                if (params[1] > that.dData.ucl || params[1] < that.dData.lcl) {
                  if (params[1] > that.dData.usl || params[1] < that.dData.lsl) {
                    return 'triangle';
                  }
                  return 'circle';
                } else {
                  return 'emptyCircle';
                }
              },
              symbolSize: function (params) {
                if (params[1] > that.dData.ucl || params[1] < that.dData.lcl) {
                  if (params[1] > that.dData.usl || params[1] < that.dData.lsl) {
                    return 10;
                  }
                  return 9;
                } else {
                  return 8;
                }
              },
              itemStyle: {
                color: function (params) {
                  if (params.data[1] > that.dData.ucl || params.data[1]< that.dData.lcl) {
                    if (params.data[1] > that.dData.usl || params.data[1] < that.dData.lsl) {
                      return red;
                    }
                    return yellow;
                  } else {
                    return green;
                  }
                }
              },
              //定义规格线
              markLine: {
                silent: true,
                symbol: 'none',
                data: [
                  {
                    name: 'USL',
                    yAxis: that.dData.usl,
                    lineStyle: {color: red},
                    label: {color: red, formatter: 'USL:' + that.dData.usl, fontSize: 10}
                  },
                  {
                    name: 'UCL',
                    yAxis: that.dData.ucl,
                    lineStyle: {color: yellow},
                    label: {color: yellow, formatter: 'UCL:' + that.dData.ucl, fontSize: 10}
                  },

                  {
                    name: 'CL',
                    yAxis: that.dData.cl,
                    lineStyle: {color: green},
                    label: {color: green, formatter: 'CL:' + that.dData.cl, fontSize: 10}
                  },
                  {
                    name: 'LCL',
                    yAxis: that.dData.lcl,
                    lineStyle: {color: yellow},
                    label: {color: yellow, formatter: 'LCL:' + that.dData.lcl, fontSize: 10}
                  },
                  {
                    name: 'LSL',
                    yAxis: that.dData.lsl,
                    lineStyle: {color: red},
                    label: {color: red, formatter: 'LSL:' + that.dData.lsl, fontSize: 10}
                  }
                ]
              }
            }
          ]
        };
      }
      return option;
    }
  },

};
</script>

<style scoped>
.button-container {
  display: grid;
  grid-template-columns: 1fr; /* 单列 */
  grid-auto-rows: minmax(auto, auto); /* 自动行高 */
  row-gap: 10px; /* 行间距 */
  /* 可以根据需要添加更多样式 */
}

.button-container .el-button {
  margin-right: 10px;
  margin-left: 10px;
}
</style>


utils.js

//计算正态曲线
export function fxNormalDistribution(array, std, mean) {
  let valueList = [];
  for (let i = 0; i < array.length; i++) {
    let ND =
      Math.sqrt(2 * Math.PI) *
      std *
      Math.pow(
        Math.E,
        -(Math.pow(array[i] - mean, 2) / (2 * Math.pow(std, 2)))
      );
    valueList.push(ND.toFixed(3));
  }
  return valueList;
}

//计算标准差
export function getStd(data, mean) {
  let sumXY = function (x, y) {
    return Number(x) + Number(y);
  };
  let square = function (x) {
    return Number(x) * Number(x);
  };
  let deviations = data.map(function (x) {
    return x - mean;
  });
  return Math.sqrt(deviations.map(square).reduce(sumXY) / (data.length - 1));
}


//对有序数组求中位数
export function getMedianSorted(arr) {
  // 获取数组长度
  let len = arr.length;

  // 如果没有元素,返回undefined或你可以返回其他合适的值
  if (len === 0) {
    return undefined;
  }

  // 如果只有一个元素,那么它就是中位数
  if (len === 1) {
    return arr[0];
  }

  // 如果数组长度是奇数,返回中间的数
  if (len % 2 === 1) {
    return arr[Math.floor(len / 2)];
  }

  // 如果数组长度是偶数,返回中间两个数的平均值
  else {
    let mid1 = arr[len / 2 - 1];
    let mid2 = arr[len / 2];
    return (mid1 + mid2) / 2.0;
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1522196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux学习——线程池的创建

一&#xff0c;线程池的作用和优点 线程池使用的是一种池化技术&#xff0c;当我们要使用线程时采用线程池创建就一次创建多个线程&#xff0c;在调用当前线程时就让其它的线程进行等待。这样做的优点有如下几点&#xff1a; 1&#xff0c;提高响应速度。线程池提前把线程创建好…

使用Laravel框架创建项目

1.使用Composer创建项目 composer create-project --prefer-dist laravel/laravel blog "5.5.*" 如下图所以&#xff0c;Laravel框架就安装完成了 安装完成后&#xff0c;进入到项目文件夹根目录&#xff0c;打开终端&#xff0c;输入php artisan serve运行项目 p…

Linux操作系统裸机开发-环境搭建

一、配置SSH服务 1、下载安装ssh服务输入以下命令 sudo apt-get install nfs-kernel-server portmap2、建立一个供SSH服务使用的文件夹如以下命令 mkdir linux 3、完成前两步之后需要将其文件路径放到/etc/exports文件里输入以下命令&#xff1a; sudo vi /etc/esports 4.打…

天天说微服务,天天开发RESTful API,那你知道RESTful API是什么东东吗?

RESTful API&#xff08;Representational State Transfer&#xff09;是一种基于网络的架构风格&#xff0c;用于设计和构建Web服务。它是一种轻量级的架构&#xff0c;可以通过HTTP协议进行通信&#xff0c;并支持各种数据格式&#xff0c;例如JSON和XML。 在现代的Web应用程…

三极管工作原理及典型电路

一、三极管的工作原理 三极管&#xff0c;也被称为双极型晶体管或晶体三极管&#xff0c;是一种电流控制元件。主要功能是将微弱的电信号放大成幅度值较大的电信号&#xff0c;工作在饱和区和截止区时同时也被用作无触点开关。 根据结构和工作原理的不同&#xff0c;三极管可以…

Jmeter---分布式

分布式&#xff1a;多台机协作&#xff0c;以集群的方式完成测试任务&#xff0c;可以提高测试效率。 分布式架构&#xff1a;控制机&#xff08;分发任务&#xff09;与多台执行机&#xff08;执行任务&#xff09; 环境搭建&#xff1a; 不同的测试机上安装 Jmeter 配置基…

代码随想录|Day22|回溯02|216.组合总和III、17.电话号码的字母组合

216.组合总和III 本题思路和 77. 组合 类似&#xff0c;在此基础上多了一个和为 n 的判断。 class Solution:def combinationSum3(self, k: int, n: int) -> List[List[int]]:def backtrack(start, path, currentSum):# 递归终止条件&#xff1a;到达叶子节点# 如果和满足条…

HTTPS证书很贵吗?

首先&#xff0c;我们需要明确一点&#xff0c;HTTPS证书的价格并不是一成不变的&#xff0c;它受到多种因素的影响。其中最主要的因素包括证书的类型、颁发机构以及所需的验证级别。 从类型上来看&#xff0c;HTTPS证书主要分为单域名证书、多域名证书和通配符证书。单域名证书…

mmz批量多页抓取数据-AES.CBC算法-爬虫

目标&#xff1a;mmz多页下载 方法&#xff1a;加一个for循环实现多页的下载 问题&#xff1a;浏览器传输服务器时对页码参数做了加密处理 解决方法&#xff1a; 1、判断加密算法模式&#xff08;mmz是AES-CBC算法&#xff09; 2、找到加密的key和iv 代码&#xff1a; i…

基于springboot+vue实现疫情防控物资调配系统项目【项目源码】计算机毕业设计

基于springbootvue实现疫情防控物资调配系统演示 B/S结构的介绍 在确定了项目的主题和研究背景之后&#xff0c;就要确定本系统的架构了。主流的架构有两种&#xff0c;一种是B/S架构&#xff0c;一种是C/S架构。C/S的全称是Client/Server&#xff0c;Client是客户端的意思&am…

HarmonyOS NEXT应用开发—Grid和List内拖拽交换子组件位置

介绍 本示例分别通过onItemDrop()和onDrop()回调&#xff0c;实现子组件在Grid和List中的子组件位置交换。 效果图预览 使用说明&#xff1a; 拖拽Grid中子组件&#xff0c;到目标Grid子组件位置&#xff0c;进行两者位置互换。拖拽List中子组件&#xff0c;到目标List子组件…

插入排序:一种简单而有效的排序算法

插入排序&#xff1a;一种简单而有效的排序算法 一、什么是插入排序&#xff1f;二、插入排序的步骤三、插入排序的C语言实现四、插入排序的性能分析五、插入排序的优化六、总结 在我们日常生活和工作中&#xff0c;排序是一种非常常见的操作。比如&#xff0c;我们可能需要对一…

MasterPDF 强大的多功能软件

哈喽呀&#xff0c;我是苏音今天给大家带来一期免费PDF的工具&#xff0c;可以实现你的大部分需求。 最近有PDF文档相关的的需求&#xff0c;但是之前一直在用WPS&#xff0c;就看能不能实现下面两个功能 1.导出指定页的PDF 2.在某一页PDF中加入指定图片 虽然WPS可以实现将…

免费接口调用 招标信息自动抽取|招标信息|招标数据解析接口

一、开源项目介绍 一款多模态AI能力引擎&#xff0c;专注于提供自然语言处理&#xff08;NLP&#xff09;、情感分析、实体识别、图像识别与分类、OCR识别和语音识别等接口服务。该平台功能强大&#xff0c;支持本地化部署&#xff0c;并鼓励用户体验和开发者共同完善&#xf…

SpringBoot整合Seata注册到Nacos服务

项目引入pom文件 <!-- SpringCloud Seata 组件--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-seata</artifactId><version>${alibaba.seata}</version><exclusions><exc…

Postman接口测试之断言,全网最细教程没有之一!

一、断言 在 postman 中我们是在Tests标签中编写断言&#xff0c;同时右侧封装了常用的断言&#xff0c;当然 Tests 除了可以作为断言&#xff0c;还可以当做后置处理器来编写一些后置处理代码&#xff0c;经常应用于&#xff1a; 【1】获取当前接口的响应&#xff0c;传递给…

智慧城市革命,物联网技术如何改变城市治理与生活方式

随着科技的不断进步&#xff0c;智慧城市已经成为现代城市发展的重要方向之一。物联网技术作为智慧城市的重要支撑&#xff0c;正深刻改变着城市的治理模式和居民的生活方式。本文将探讨智慧城市革命&#xff0c;以及物联网技术如何改变城市治理与生活方式&#xff0c;同时介绍…

c++入门学习⑨——STL(万字总结,超级超级详细版)看完这一篇就够了!!!

目录 &#x1f384;前言 &#x1f384;概念 引入 定义 优点 &#x1f384;六大组件 容器 算法 迭代器 仿函数 适配器 空间配置器 &#x1f384;三大组件 迭代器&#xff08;iterator&#xff09; 定义 分类&#xff1a; 正向迭代器&#xff1a; 常量正向迭代…

c语言:操作符详解(上)

目录 一、操作符的分类二、二进制和进制转换1.2进制转10进制2.10进制转2进制3.2进制转8进制4.2进制转16进制 三、原码、反码、补码四、算术操作符、-、*、/、%1.**和-**2.*3./4.% 五、移位操作符1.左移操作符2.右移操作符 六、位操作符&#xff1a;&、|、^、~七、赋值操作符…

口腔管理平台 |基于springboot框架+ Mysql+Java+B/S结构的口腔管理平台 设计与实现(可运行源码+数据库+lw文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 会员功能 系统功能设计 数据库E-R图设计 lunwen参考…