增加SPC扩展控制图验证

This commit is contained in:
zhangshun
2026-06-16 10:24:33 +08:00
parent e9c8bde14a
commit 41381b9577

View File

@@ -556,6 +556,34 @@
</div>
<div class="chart empty" id="histogramChart">等待执行</div>
</div>
<div class="chart-card wide">
<div class="chart-title">
<span>I-MR 单值-移动极差图</span>
<span>I-MR</span>
</div>
<div class="chart empty" id="imrChart">等待执行</div>
</div>
<div class="chart-card wide">
<div class="chart-title">
<span>Run Chart 趋势图</span>
<span>Run</span>
</div>
<div class="chart empty" id="runChart">等待执行</div>
</div>
<div class="chart-card wide">
<div class="chart-title">
<span>Median-R 中位数-极差图</span>
<span>Median-R</span>
</div>
<div class="chart empty" id="medianRChart">等待执行</div>
</div>
<div class="chart-card wide">
<div class="chart-title">
<span>EWMA 指数加权移动平均图</span>
<span>EWMA</span>
</div>
<div class="chart empty" id="ewmaChart">等待执行</div>
</div>
</div>
<div class="table-wrap">
@@ -690,6 +718,10 @@
xs: document.getElementById("xsChart"),
capability: document.getElementById("capabilityChart"),
histogram: document.getElementById("histogramChart"),
imr: document.getElementById("imrChart"),
run: document.getElementById("runChart"),
medianR: document.getElementById("medianRChart"),
ewma: document.getElementById("ewmaChart"),
};
let wasmModule = null;
@@ -756,6 +788,10 @@
xs: echarts.init(chartElements.xs),
capability: echarts.init(chartElements.capability),
histogram: echarts.init(chartElements.histogram),
imr: echarts.init(chartElements.imr),
run: echarts.init(chartElements.run),
medianR: echarts.init(chartElements.medianR),
ewma: echarts.init(chartElements.ewma),
};
window.addEventListener("resize", () => {
@@ -1172,9 +1208,244 @@
};
}
// 计算输入数据的中位数。
function median(values) {
if (!values.length) return 0;
const sorted = [...values].sort((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return (sorted[middle - 1] + sorted[middle]) / 2;
}
return sorted[middle];
}
// 计算 I-MR 单值和移动极差控制图数据。
function calculateImrData(values) {
const movingRanges = values
.slice(1)
.map((value, index) => Math.abs(value - values[index]));
const center = mean(values);
const mrBar = mean(movingRanges);
const sigma = mrBar / 1.128;
const upper = center + 3 * sigma;
const lower = center - 3 * sigma;
return {
values: values.map(round3),
movingRanges: movingRanges.map(round3),
center: round3(center),
upper: round3(upper),
lower: round3(lower),
mrCenter: round3(mrBar),
mrUpper: round3(3.267 * mrBar),
mrLower: 0,
};
}
// 计算 Median-R 中位数和极差控制图数据。
function calculateMedianRData(groups, subgroupSize) {
const coeff = coefficients[subgroupSize];
const medians = groups.map(median);
const ranges = groups.map(range);
const center = mean(medians);
const rBar = mean(ranges);
return {
medians: medians.map(round3),
ranges: ranges.map(round3),
center: round3(center),
upper: round3(center + coeff.A2 * rBar),
lower: round3(center - coeff.A2 * rBar),
rCenter: round3(rBar),
rUpper: round3(coeff.D4 * rBar),
rLower: round3(coeff.D3 * rBar),
};
}
// 计算 EWMA 控制图数据,默认 lambda 取 0.2。
function calculateEwmaData(values, lambda = 0.2) {
const center = mean(values);
const movingRanges = values
.slice(1)
.map((value, index) => Math.abs(value - values[index]));
const sigma = mean(movingRanges) / 1.128;
const ewma = [];
let previous = center;
values.forEach((value) => {
previous = lambda * value + (1 - lambda) * previous;
ewma.push(previous);
});
const factor = Math.sqrt(lambda / (2 - lambda));
return {
values: ewma.map(round3),
center: round3(center),
upper: round3(center + 3 * sigma * factor),
lower: round3(center - 3 * sigma * factor),
lambda,
};
}
// 创建 I-MR 控制图配置。
function createImrOption(imr) {
const valueLabels = sequenceLabels(imr.values.length);
const mrLabels = sequenceLabels(imr.movingRanges.length);
return {
color: ["#126b5c", "#b45f06", "#8a5a00", "#b42318"],
tooltip: { trigger: "axis" },
legend: { top: 0, right: 0 },
grid: [
{ left: 48, right: 24, top: 42, height: 82 },
{ left: 48, right: 24, top: 188, height: 72 },
],
xAxis: [
{ type: "category", gridIndex: 0, data: valueLabels, axisLabel: { color: "#65736e" } },
{ type: "category", gridIndex: 1, data: mrLabels, axisLabel: { color: "#65736e" } },
],
yAxis: [
{
type: "value",
name: "I",
gridIndex: 0,
scale: true,
axisLabel: { color: "#65736e" },
splitLine: { lineStyle: { color: "#e5ebe8" } },
},
{
type: "value",
name: "MR",
gridIndex: 1,
scale: true,
axisLabel: { color: "#65736e" },
splitLine: { lineStyle: { color: "#e5ebe8" } },
},
],
series: [
{ name: "单值", type: "line", data: imr.values, xAxisIndex: 0, yAxisIndex: 0, symbolSize: 5 },
{ name: "CL_I", type: "line", data: constantSeries(imr.values.length, imr.center), xAxisIndex: 0, yAxisIndex: 0, symbol: "none", lineStyle: { type: "dashed" } },
{ name: "UCL/LCL_I", type: "line", data: constantSeries(imr.values.length, imr.upper), xAxisIndex: 0, yAxisIndex: 0, symbol: "none", lineStyle: { type: "dotted" } },
{ name: "UCL/LCL_I", type: "line", data: constantSeries(imr.values.length, imr.lower), xAxisIndex: 0, yAxisIndex: 0, symbol: "none", lineStyle: { type: "dotted" } },
{ name: "移动极差", type: "bar", data: imr.movingRanges, xAxisIndex: 1, yAxisIndex: 1, barMaxWidth: 14 },
{ name: "CL_MR", type: "line", data: constantSeries(imr.movingRanges.length, imr.mrCenter), xAxisIndex: 1, yAxisIndex: 1, symbol: "none", lineStyle: { type: "dashed" } },
{ name: "UCL_MR", type: "line", data: constantSeries(imr.movingRanges.length, imr.mrUpper), xAxisIndex: 1, yAxisIndex: 1, symbol: "none", lineStyle: { type: "dotted" } },
],
};
}
// 创建 Run Chart 趋势图配置。
function createRunChartOption(values) {
const roundedValues = values.map(round3);
const center = round3(median(values));
return {
color: ["#126b5c", "#b45f06"],
tooltip: { trigger: "axis" },
legend: { top: 0, right: 0 },
grid: { left: 48, right: 24, top: 42, bottom: 38 },
xAxis: {
type: "category",
data: sequenceLabels(values.length),
axisLabel: { color: "#65736e" },
},
yAxis: {
type: "value",
scale: true,
axisLabel: { color: "#65736e" },
splitLine: { lineStyle: { color: "#e5ebe8" } },
},
series: [
{
name: "原始序列",
type: "line",
data: roundedValues,
symbolSize: 4,
},
{
name: "中位线",
type: "line",
data: constantSeries(values.length, center),
symbol: "none",
lineStyle: { type: "dashed" },
},
],
};
}
// 创建 Median-R 控制图配置。
function createMedianROption(medianR) {
return createControlChartOption(
"Median-R",
medianR.medians,
medianR.ranges,
{
CL_X: medianR.center,
UCL_X: medianR.upper,
LCL_X: medianR.lower,
CL_R: medianR.rCenter,
UCL_R: medianR.rUpper,
LCL_R: medianR.rLower,
},
"R",
);
}
// 创建 EWMA 控制图配置。
function createEwmaOption(ewma) {
return {
color: ["#126b5c", "#b45f06", "#b42318"],
tooltip: { trigger: "axis" },
legend: { top: 0, right: 0 },
grid: { left: 48, right: 24, top: 42, bottom: 38 },
xAxis: {
type: "category",
data: sequenceLabels(ewma.values.length),
axisLabel: { color: "#65736e" },
},
yAxis: {
type: "value",
scale: true,
axisLabel: { color: "#65736e" },
splitLine: { lineStyle: { color: "#e5ebe8" } },
},
series: [
{
name: `EWMA λ=${ewma.lambda}`,
type: "line",
data: ewma.values,
smooth: true,
symbolSize: 4,
},
{
name: "CL",
type: "line",
data: constantSeries(ewma.values.length, ewma.center),
symbol: "none",
lineStyle: { type: "dashed" },
},
{
name: "UCL/LCL",
type: "line",
data: constantSeries(ewma.values.length, ewma.upper),
symbol: "none",
lineStyle: { type: "dotted" },
},
{
name: "UCL/LCL",
type: "line",
data: constantSeries(ewma.values.length, ewma.lower),
symbol: "none",
lineStyle: { type: "dotted" },
},
],
};
}
// 使用 WASM 返回结果刷新全部 ECharts 图形。
function renderCharts(spcResult) {
function renderCharts(spcResult, input) {
const chartMap = ensureCharts();
const groups = buildGroups(input.x, input.n);
const imr = calculateImrData(input.x);
const medianR = calculateMedianRData(groups, input.n);
const ewma = calculateEwmaData(input.x);
chartMap.xr.setOption(
createControlChartOption(
"XR",
@@ -1197,6 +1468,10 @@
);
chartMap.capability.setOption(createCapabilityOption(spcResult.Cpk), true);
chartMap.histogram.setOption(createHistogramOption(spcResult.Cpk), true);
chartMap.imr.setOption(createImrOption(imr), true);
chartMap.run.setOption(createRunChartOption(input.x), true);
chartMap.medianR.setOption(createMedianROption(medianR), true);
chartMap.ewma.setOption(createEwmaOption(ewma), true);
}
// 更新输入数据数量显示。
@@ -1329,7 +1604,7 @@
renderJsonOutputs(response, localResult);
renderComparison(response.res_data, localResult);
renderCharts(response.res_data);
renderCharts(response.res_data, input);
setMessage("验证完成", "is-ok");
} catch (error) {
verifyStatus.textContent = "失败";