Просмотр исходного кода

[task-1853] 性能验证模块 开发 (二期) / 线性区间验证:平均斜率评价法

Li Yuan 1 год назад
Родитель
Сommit
32a776eade

+ 23 - 3
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/components/verification/model/ConfigVO.java

@@ -4,7 +4,7 @@ public class ConfigVO {
 
 
     private String[] model;
-    private int range;
+    private double[] range;
     private String standard;
     private String remark;
     private double tea;
@@ -20,6 +20,10 @@ public class ConfigVO {
     private int rejectionRate;
 
     private boolean isSupportItems;
+
+    private double allowableR2;
+
+    private double rangeValue;
     public String[] getModel() {
         return model;
     }
@@ -28,11 +32,11 @@ public class ConfigVO {
         this.model = model;
     }
 
-    public int getRange() {
+    public double[] getRange() {
         return range;
     }
 
-    public void setRange(int range) {
+    public void setRange(double[] range) {
         this.range = range;
     }
 
@@ -132,6 +136,22 @@ public class ConfigVO {
         this.rejectionRate = rejectionRate;
     }
 
+    public double getAllowableR2() {
+        return allowableR2;
+    }
+
+    public void setAllowableR2(double allowableR2) {
+        this.allowableR2 = allowableR2;
+    }
+
+    public double getRangeValue() {
+        return rangeValue;
+    }
+
+    public void setRangeValue(double rangeValue) {
+        this.rangeValue = rangeValue;
+    }
+
     public boolean isSupportItems() {
         return isSupportItems;
     }

+ 87 - 12
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/components/verification/model2/LinearRangeAverageSlope.java

@@ -1,11 +1,17 @@
 package com.lc.ibps.components.verification.model2;
 
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.lc.ibps.components.verification.model.InspectionConfigVO;
+import com.lc.ibps.components.verification.report.ChartDTO;
 import com.lc.ibps.components.verification.report.TableDTO;
+import org.apache.commons.math3.stat.StatUtils;
 import org.apache.commons.math3.stat.regression.SimpleRegression;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class LinearRangeAverageSlope extends PVModel {
 
@@ -14,15 +20,27 @@ public class LinearRangeAverageSlope extends PVModel {
     private double slopeCI;//ConfidenceInterval
     private double rSquare;
     private double r;
+    private double[] targetValues;
+    private double[] means;
+    private double allowableR2;
 
     public LinearRangeAverageSlope(double[][] data, InspectionConfigVO configVO) {
         super(data, configVO);
+        this.targetValues = configVO.getTargetValue();
+        this.allowableR2 = configVO.getAllowableR2();
+        this.means = new double[data.length];
+        for (int i = 0; i < data.length; i++) {
+            means[i] = StatUtils.mean(data[i]);
+        }
     }
 
     @Override
     public void calculate() {
         final SimpleRegression regression = new SimpleRegression();
-        regression.addData(data);
+        for (int i = 0; i < means.length; i++) {
+
+            regression.addData(targetValues[i],means[i]);
+        }
         slope = regression.getSlope();
         intercept = regression.getIntercept();
         slopeCI = regression.getSlopeConfidenceInterval();
@@ -31,7 +49,45 @@ public class LinearRangeAverageSlope extends PVModel {
 
     }
 
+    @Override
+    TableDTO buildTable1DTO(int sheetIndex) {
+
+        TableDTO table = new TableDTO();
+        String[] header = {"样本名", "理论值","实测均值", "偏差", "百分偏差%"};
+        table.buildHeader(header);
+
+
+        Object[][] r = new Object[specimensNum][5];
+
+        for (int i = 0; i < r.length; i++) {
+            r[i][0] = specimensName[i];
+            r[i][1] = format(getTargetValues()[i],getScale());
+            r[i][2] = format(getMeans()[i],getScale());
+            r[i][3] = format(getMeans()[i] - getTargetValues()[i],getScale());
+            r[i][4] = format(100 * (getMeans()[i] - getTargetValues()[i]) /getTargetValues()[i],1);
+        }
+        table.buildData(r);
+        return table;
+    }
+    @Override
+    public String[] tableNames() {
+        return new String[]{"表1: 线性范围计算结果"};
+    }
+
+    @Override
+    ChartDTO buildChart1DTO(int sheetIndex) {
+        ChartDTO chartDTO = new ChartDTO("polynomialRegression");
+        double[][] data = new double[targetValues.length][2];
+        for (int i = 0; i < data.length; i++) {
+            data[i] = new double[]{format(targetValues[i],getScale()), format(means[i],getScale())};
+        }
+//        chartDTO.setData(data);
+        Map<String, Object> map = new HashMap<>();
+        map.put("data", JSONObject.toJSONString(data, SerializerFeature.DisableCircularReferenceDetect));
 
+        chartDTO.setOption(renderChartTemplate(map, 0, "/scatter/polynomialRegression.ftl"));
+        return chartDTO;
+    }
     public TableDTO buildDataTableDTO() {
         List<String> header = new ArrayList<String>();
 
@@ -52,14 +108,9 @@ public class LinearRangeAverageSlope extends PVModel {
     }
 
 
-    @Override
-    public String[] tableNames() {
-        return new String[0];
-    }
-
     @Override
     public String[] chartNames() {
-        return new String[0];
+        return new String[]{"图1: 线性评价散点图"};
     }
 
     public String getFunction() {
@@ -68,14 +119,14 @@ public class LinearRangeAverageSlope extends PVModel {
 
         return "y =" +
                 (b0 >= 0 ? " " : " -") +
-                Math.abs(b0) +
+                Math.abs(format(b0,getScale())) +
                 (b1 > 0 ? " + " : " - ") +
-                Math.abs(b1) +
+                Math.abs(format(b1,getScale())) +
                 "x";
     }
 
     public String getSlopeCIFormat(){
-        return String.format("%s ~ %s)", slope - slopeCI,slope + slopeCI);
+        return String.format("%s ~ %s)", format(slope - slopeCI,getScale()),format(slope + slopeCI,getScale()));
     }
 
     public double getSlope() {
@@ -103,7 +154,7 @@ public class LinearRangeAverageSlope extends PVModel {
     }
 
     public double getrSquare() {
-        return rSquare;
+        return format(rSquare,getScale());
     }
 
     public void setrSquare(double rSquare) {
@@ -111,10 +162,34 @@ public class LinearRangeAverageSlope extends PVModel {
     }
 
     public double getR() {
-        return r;
+        return format(r,getScale());
     }
 
     public void setR(double r) {
         this.r = r;
     }
+
+    public double[] getTargetValues() {
+        return targetValues;
+    }
+
+    public void setTargetValues(double[] targetValues) {
+        this.targetValues = targetValues;
+    }
+
+    public double[] getMeans() {
+        return means;
+    }
+
+    public void setMeans(double[] means) {
+        this.means = means;
+    }
+
+    public double getAllowableR2() {
+        return allowableR2;
+    }
+
+    public void setAllowableR2(double allowableR2) {
+        this.allowableR2 = allowableR2;
+    }
 }

+ 66 - 5
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/components/verification/model2/LinearRangeRecoveryMethod.java

@@ -2,31 +2,92 @@ package com.lc.ibps.components.verification.model2;
 
 import com.lc.ibps.components.verification.model.InspectionConfigVO;
 import com.lc.ibps.components.verification.report.TableDTO;
+import org.apache.commons.math3.stat.StatUtils;
 
 public class LinearRangeRecoveryMethod extends LinearRangeAverageSlope {
 
+    private double[] range;
+    private double[] recoveryRate;
+    private double min;
+    private double max;
 
     public LinearRangeRecoveryMethod(double[][] data, InspectionConfigVO configVO) {
         super(data, configVO);
-
-
+        this.range = configVO.getRange();
+        this.recoveryRate = new double[specimensNum];
     }
 
     @Override
     public void calculate() {
         super.calculate();
+        for (int i = 0; i < specimensNum; i++) {
+            recoveryRate[i] = format(100d*getMeans()[i]/getTargetValues()[i],1);
+        }
+        min = StatUtils.min(recoveryRate);
+        max = StatUtils.max(recoveryRate);
     }
 
+    @Override
+    TableDTO buildTable1DTO(int sheetIndex) {
+
+        TableDTO table = new TableDTO();
+        String[] header = {"样本名", "理论值","实测均值", "稀释回收率%", "稀释偏差%"};
+        table.buildHeader(header);
+
+
+        Object[][] r = new Object[specimensNum][5];
 
-    public TableDTO buildDataTableDTO() {
-        return null;
+        for (int i = 0; i < r.length; i++) {
+            r[i][0] = specimensName[i];
+            r[i][1] = format(getTargetValues()[i],getScale());
+            r[i][2] = format(getMeans()[i],getScale());
+
+            r[i][3] = recoveryRate[i];
+            r[i][4] = format(100d - recoveryRate[i],1);
+        }
+        table.buildData(r);
+        return table;
+    }
+    @Override
+    public String[] tableNames() {
+        return new String[]{"表1: 每个浓度样品稀释回收率结果"};
     }
 
 
     @Override
     public String generateResult() {
-        return renderReportTemplate(this, null);
+        return renderReportTemplate(this, "/report/linearRangeRecoveryMethod.ftl");
     }
 
+    public double[] getRange() {
+        return range;
+    }
+
+    public void setRange(double[] range) {
+        this.range = range;
+    }
 
+    public double[] getRecoveryRate() {
+        return recoveryRate;
+    }
+
+    public void setRecoveryRate(double[] recoveryRate) {
+        this.recoveryRate = recoveryRate;
+    }
+
+    public double getMin() {
+        return min;
+    }
+
+    public void setMin(double min) {
+        this.min = min;
+    }
+
+    public double getMax() {
+        return max;
+    }
+
+    public void setMax(double max) {
+        this.max = max;
+    }
 }

+ 3 - 2
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/components/verification/model2/TruenessEP15Patient.java

@@ -233,16 +233,17 @@ public class TruenessEP15Patient extends PVModel {
         ChartDTO chartDTO = new ChartDTO("linesForRange");
         double[][] data = new double[yx.length][2];
         for (int i = 0; i < data.length; i++) {
-            data[i] = new double[]{x[i], yx[i]};
+            data[i] = new double[]{x[i], format(yx[i],getScale())};
         }
 //        chartDTO.setData(data);
         final HashMap<String, Object> config = new HashMap<>();
         config.put("yAxisUp", bias);
         config.put("xAxisName","比较方法");
+        config.put("xAxisMin",Math.floor(StatUtils.min(x)));
         config.put("yAxisName","偏倚(实验-比较)");
 //        config.put("yAxisLow", -sdClaim);
         config.put("data", JSONObject.toJSONString(data, SerializerFeature.DisableCircularReferenceDetect));
-        chartDTO.setOption(renderChartTemplate(config, 0, "/scatter/linesForRange.ftl"));
+        chartDTO.setOption(renderChartTemplate(config, 0, "/scatter/chartPrecisionRepeatability.ftl"));
         chartDTO.setNote(String.format("%s%s %s","图中实线为平均偏移值",format(bias),getUnits()));
         return chartDTO;
     }

+ 1 - 1
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/components/verification/report/TableDTO.java

@@ -43,7 +43,7 @@ public class TableDTO {
         buildHeader(h, null);
     }
 
-    public void buildData(String[][] data) {
+    public void buildData(Object[][] data) {
         list = new Map[data.length];
         for (int i = 0; i < data.length; i++) {
             HashMap<String, Object> m = new HashMap<>();

+ 12 - 2
ibps-provider-root/modules/provider-business/src/main/resources/pv/report/linearRangeAverageSlope.ftl

@@ -1,4 +1,14 @@
 <h3>结论判断:</h3>
-线性回归方程式 ${function},R<sub>2</sub>=${rSquare}, r=${r},
+线性回归方程式 ${function},R<sup>2</sup>=${rSquare}, r=${r},
 斜率的95%可信区间为(${slopeCIFormat})
-R>0.975
+<br/>
+<#if (rSquare < allowableR2)>
+R<sup>2</sup> < ${allowableR2}, 因此这些实验数据不能证实线性度。
+<#else>
+R<sup>2</sup> ≥ ${allowableR2},
+    <#if (slope - slopeCI > 1) || (slope + slopeCI < 1)>
+但斜率的95%可信区间不包括1,因此这些实验数据不能证实线性度。
+    <#else>
+并且斜率的95%可信区间包括1,即可确定呈线性。
+    </#if>
+</#if>

+ 22 - 0
ibps-provider-root/modules/provider-business/src/main/resources/pv/report/linearRangeRecoveryMethod.ftl

@@ -0,0 +1,22 @@
+<h3>结论判断:</h3>
+线性回归方程式 ${function},R<sup>2</sup>=${rSquare}, r=${r},
+斜率的95%可信区间为(${slopeCIFormat})
+<br/>
+<#if (rSquare < allowableR2)>
+    R<sup>2</sup> < ${allowableR2}, 因此这些实验数据不能证实线性度。
+<#else>
+    R<sup>2</sup> ≥ ${allowableR2},
+    <#if (slope - slopeCI > 1) || (slope + slopeCI < 1)>
+        但斜率的95%可信区间不包括1,因此这些实验数据不能证实线性度。
+    <#else>
+        并且斜率的95%可信区间包括1,即可确定呈线性。
+    </#if>
+</#if>
+<br />
+回收率在${min}% ~  ${max}%,
+<#if (min < range[0]) || (max > range[1]) >
+不满足
+<#else>
+满足
+</#if>
+可接受范围${range[0]}% ~  ${range[1]}%的要求。

+ 89 - 0
ibps-provider-root/modules/provider-business/src/main/resources/pv/scatter/chartPrecisionRepeatability.ftl

@@ -0,0 +1,89 @@
+{
+    grid: {
+        left: '8%',
+        right: '4%',
+        bottom: '6%',
+        top: '12%',
+        containLabel: true
+    },
+    xAxis: {
+        name: '${xAxisName}',
+        nameLocation: 'center',
+        nameTextStyle: {
+            fontSize: 14,
+            color: '#333'
+        },
+        min: ${xAxisMin},
+        nameGap: 20,
+        splitLine: {
+            lineStyle: {
+                type: 'dashed'
+            }
+        }
+    },
+    yAxis: {
+        name: '${yAxisName}',
+        nameLocation: 'center',
+        nameTextStyle: {
+            fontSize: 14,
+            color: '#333'
+        },
+        nameGap: 30,
+        splitLine: {
+            lineStyle: {
+                type: 'dashed'
+            }
+        }
+    },
+    title: {
+        text: '散点图',
+        left: 'center',
+        top: 8,
+        textStyle: {
+            fontSize: 14,
+            fontWeight: 'bold',
+            color: '#333'
+        }
+    },
+    series: [
+        {
+            data: ${data},
+            type: 'scatter',
+            name: '散点',
+            tooltip: {
+                formatter: '{a} <br/>{b} : {c}'
+            },
+            emphasis: {
+                focus: 'self',
+                label: {
+                    show: true,
+                    formatter: '{c}',
+                    fontSize: 14,
+                    color: '#000',
+                    position: 'top'
+                }
+            },
+            markLine: {
+                symbol: ['none'],
+                itemStyle: {
+                    normal: {
+                        lineStyle: {
+                            type: 'solid',
+                            color: '#409eff'
+                        },
+                        label: {
+                            show: true,
+                            position: 'middle',
+                            formatter: '{b}'
+                        }
+                    }
+                },
+                data: [{
+                    name: 'Y 轴值为 ${yAxisUp} 的水平线',
+                    yAxis: ${yAxisUp},
+                    valueDim: 'close'
+                }]
+            }
+        }
+    ]
+}

+ 5 - 3
ibps-provider-root/modules/provider-business/src/test/java/com/lc/ibps/components/verification/model2/LinearRangeAverageSlopeTest.java

@@ -25,17 +25,19 @@ public class LinearRangeAverageSlopeTest {
 
     @Test
     public void calculate() {
-        InspectionConfigVO config = new InspectionConfigVO("平均斜率评价法", 1, 6,
-                null, 2, LocalDate.now(), true);
+        InspectionConfigVO config = new InspectionConfigVO("平均斜率评价法", 1, 11,
+                null, 3, LocalDate.now(), true);
 
         config.setDecimal(3);
         config.setUnits("mmg/L");
         config.setKey(ModelEnum.LinearRangeAverageSlope.getKey());
+        config.setTargetValue(new double[]{3,5,7,9,11,13,15,17,19,21,23});
+        config.setAllowableR2(0.995);
         RandomUtils.nextDouble();
         double[][] data = new double[config.getSpecimensNum()][config.getRepeatNum()];
         for (int i = 0; i < config.getSpecimensNum(); i++) {
             for (int j = 0; j < config.getRepeatNum(); j++) {
-                data[i][j] = RandomUtils.nextDouble(3.12+i*2, 3.75+i*2);
+                data[i][j] = RandomUtils.nextDouble(0+i*2, 6+i*2);
             }
         }
         RealMatrix matrix = MatrixUtils.createRealMatrix(data);

+ 1 - 1
ibps-provider-root/modules/provider-business/src/test/java/com/lc/ibps/components/verification/model2/PrecisionEP15Test.java

@@ -27,7 +27,7 @@ public class PrecisionEP15Test {
     public void calculate() {
         InspectionConfigVO config = new InspectionConfigVO("精密度", 5, 2,
                 null, 3, LocalDate.now(), true);
-        config.setRange(99);
+        config.setRejectionRate(1);
 //        config.setBatchCVSValue(3.2);
 //        config.setDailyCVSValue(3.1);
         config.setAllowableSDl(new double[]{0.01,0.02});