工程安全监测

1:多项式回归
2:预报
3:数据校验
master
yangzhe123 2025-11-17 11:49:31 +08:00
parent cfd8dda870
commit bc008b0174
19 changed files with 1695 additions and 9 deletions

View File

@ -11,6 +11,7 @@ import com.gunshi.project.hsz.common.model.so.JcskSyRPageSo;
import com.gunshi.project.hsz.common.model.so.OsmoticDetailQuerySo; import com.gunshi.project.hsz.common.model.so.OsmoticDetailQuerySo;
import com.gunshi.project.hsz.common.model.so.OsmoticDetailQuerySo; import com.gunshi.project.hsz.common.model.so.OsmoticDetailQuerySo;
import jakarta.validation.constraints.Size;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
@ -362,4 +363,25 @@ GROUP BY dm
</script> </script>
""") """)
Page<JcskSyR> artificialPage(Page<Object> page,@Param("dto") JcskSyRPageSo page1); Page<JcskSyR> artificialPage(Page<Object> page,@Param("dto") JcskSyRPageSo page1);
@Select("""
WITH ranked_data AS (
SELECT
tm,
rz,
ABS(EXTRACT(HOUR FROM tm) + EXTRACT(MINUTE FROM tm)/60.0 - 8) as time_diff,
ROW_NUMBER() OVER (PARTITION BY DATE(tm) ORDER BY ABS(EXTRACT(HOUR FROM tm) + EXTRACT(MINUTE FROM tm)/60.0 - 8)) as rn
FROM st_rsvr_r
WHERE stcd = #{stcd}
AND tm >= #{dto.dateTimeRangeSo.start}
AND tm <= #{dto.dateTimeRangeSo.end}
)
SELECT
TO_CHAR(DATE(tm) + INTERVAL '8 hours', 'YYYY-MM-DD HH24:MI:SS') as tm,
rz
FROM ranked_data
WHERE rn = 1
ORDER BY tm
""")
List<StRzVo> qeury8AmRz(@Param("dto") OsmoticQuerySo dto, @Param("stcd") String stcd);
} }

View File

@ -0,0 +1,90 @@
package com.gunshi.project.hsz.controller;
import com.gunshi.core.result.R;
import com.gunshi.project.hsz.common.model.vo.OsmoticPressDetailVo;
import com.gunshi.project.hsz.entity.dto.ProjectSafeCalculateDto;
import com.gunshi.project.hsz.entity.dto.ProjectSaveReportDto;
import com.gunshi.project.hsz.entity.dto.SyRegressionDataDto;
import com.gunshi.project.hsz.entity.vo.ForecastResultVo;
import com.gunshi.project.hsz.model.ForecastTask;
import com.gunshi.project.hsz.model.RegressionEquation;
import com.gunshi.project.hsz.model.SyRegressionData;
import com.gunshi.project.hsz.service.ForecastResultsService;
import com.gunshi.project.hsz.service.JcskSyRService;
import com.gunshi.project.hsz.service.SyRegressionDataService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@Tag(name = "工程安全监测")
@RestController
@RequestMapping(value="/projectSafe")
public class ProjectSafeAnalyseController {
@Autowired
private JcskSyRService jcskSyRService;
@Autowired
private SyRegressionDataService syRegressionDataService;
@Autowired
private ForecastResultsService forecastResultsService;
@Operation(summary = "多项式回归")
@PostMapping("/caculate")
public R<Map<String, RegressionEquation>> calculate(@RequestBody ProjectSafeCalculateDto dto){
Map<String, RegressionEquation> ans = jcskSyRService.calculate(dto);
return R.ok(ans);
}
@Operation(summary = "预报")
@PostMapping("/report")
public R<List<OsmoticPressDetailVo>> report(@RequestBody ProjectSaveReportDto projectSaveReportDto){
ForecastTask forecastTask = projectSaveReportDto.getForecastTask();
List<ForecastResultVo> voList = forecastResultsService.getHumanForecastResult(forecastTask);
SyRegressionData syRegressionData = new SyRegressionData();
syRegressionData.setDvcd(projectSaveReportDto.getDvcd());
syRegressionData.setOrder(projectSaveReportDto.getOrder());
List<SyRegressionData> queryReg = syRegressionDataService.queryData(syRegressionData);//获取方程组
if(queryReg.isEmpty()){
return R.ok(null);
}
//计算
List<OsmoticPressDetailVo> res = forecastResultsService.calculateY(queryReg.get(0),voList);
return R.ok(res);
}
@Operation(summary = "保存多线程回归方程")
@PostMapping("/save")
public R<Boolean> save(@RequestBody SyRegressionDataDto dto){
if(!dto.getList().isEmpty()){
syRegressionDataService.saveBatch(dto.getList());
}
return R.ok(true);
}
@Operation(summary = "删除多线程回归方程")
@PostMapping("/del")
public R<Boolean> delete(@RequestBody SyRegressionDataDto dto){
if(!dto.getList().isEmpty()){
syRegressionDataService.removeBatchByIds(dto.getList());
}
return R.ok(true);
}
@Operation(summary = "查找多线程回归方程")
public R<List<SyRegressionData>> list(@RequestBody SyRegressionData dto){
List<SyRegressionData> res = syRegressionDataService.queryData(dto);
return R.ok(res);
}
}

View File

@ -0,0 +1,64 @@
package com.gunshi.project.hsz.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gunshi.core.result.R;
import com.gunshi.project.hsz.common.model.JcskGnssR;
import com.gunshi.project.hsz.common.model.JcskSyB;
import com.gunshi.project.hsz.common.model.so.JcskGnssRPageSo;
import com.gunshi.project.hsz.common.validate.markers.Insert;
import com.gunshi.project.hsz.common.validate.markers.Update;
import com.gunshi.project.hsz.entity.so.SyDataCheckRulePageSo;
import com.gunshi.project.hsz.model.SyDataCheckRule;
import com.gunshi.project.hsz.service.SyDataCheckRuleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;
@Tag(name = "渗压数据清洗规则")
@RestController
@RequestMapping(value="/syDataCheckRule")
public class SyDataCheckRuleController {
@Autowired
private SyDataCheckRuleService syDataCheckRuleService;
@Operation(summary = "分页")
@PostMapping("/page")
public R<Page<SyDataCheckRule>> page(@RequestBody @Validated SyDataCheckRulePageSo page) {
return R.ok(syDataCheckRuleService.pageQuery(page));
}
@Operation(summary = "新增")
@PostMapping("/insert")
public R<SyDataCheckRule> insert(@Validated(Insert.class) @RequestBody SyDataCheckRule dto) {
return R.ok(syDataCheckRuleService.saveData(dto));
}
@Operation(summary = "修改")
@PostMapping("/update")
public R<Boolean> update(@Validated(Update.class) @RequestBody SyDataCheckRule dto) {
return R.ok(syDataCheckRuleService.updateData(dto));
}
@Operation(summary = "删除")
@GetMapping("/del/{id}")
public R<Boolean> del(@Schema(name = "id") @PathVariable("id") Serializable id) {
if (Objects.isNull(syDataCheckRuleService.getById(id))) {
throw new IllegalArgumentException("当前数据不存在");
}
return R.ok(syDataCheckRuleService.removeById(id));
}
}

View File

@ -0,0 +1,13 @@
package com.gunshi.project.hsz.entity.dto;
import com.gunshi.db.dto.DateTimeRangeSo;
import lombok.Data;
@Data
public class ProjectSafeCalculateDto {
private DateTimeRangeSo dateTimeRangeSo;//时间范围
private String dvcd;//测点编号
}

View File

@ -0,0 +1,15 @@
package com.gunshi.project.hsz.entity.dto;
import com.gunshi.project.hsz.model.ForecastTask;
import lombok.Data;
@Data
public class ProjectSaveReportDto {
private ForecastTask forecastTask;
private String dvcd;
private Integer order;
}

View File

@ -0,0 +1,13 @@
package com.gunshi.project.hsz.entity.dto;
import com.gunshi.project.hsz.model.SyRegressionData;
import lombok.Data;
import java.util.List;
@Data
public class SyRegressionDataDto {
List<SyRegressionData> list;
}

View File

@ -0,0 +1,18 @@
package com.gunshi.project.hsz.entity.so;
import com.gunshi.db.dto.PageSo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class SyDataCheckRulePageSo {
@NotNull(message = "分页参数不能为空")
@Schema(description = "分页参数")
private PageSo pageSo;
private String dvcd;
}

View File

@ -0,0 +1,9 @@
package com.gunshi.project.hsz.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gunshi.project.hsz.model.SyDataCheckRule;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SyDataCheckRuleMapper extends BaseMapper<SyDataCheckRule> {
}

View File

@ -0,0 +1,11 @@
package com.gunshi.project.hsz.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gunshi.project.hsz.model.SyRegressionData;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SyRegressionDataMapper extends BaseMapper<SyRegressionData> {
}

View File

@ -0,0 +1,106 @@
package com.gunshi.project.hsz.model;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 线
*/
@Data
public class RegressionEquation {
/**
*
*/
private int order;
/**
*
* y = ax² + bx + ccoefficients = [c, b, a]
*/
private List<BigDecimal> coefficients;
/**
* R²
*/
private BigDecimal rSquared;
/**
*
*/
private int dataCount;
/**
*
*/
private String equationString;
public RegressionEquation(int order, List<BigDecimal> coefficients, BigDecimal rSquared, int dataCount) {
this.order = order;
this.coefficients = coefficients;
this.rSquared = rSquared;
this.dataCount = dataCount;
this.equationString = generateEquationString();
}
/**
*
*/
private String generateEquationString() {
StringBuilder sb = new StringBuilder("y = ");
for (int i = coefficients.size() - 1; i >= 0; i--) {
BigDecimal coeff = coefficients.get(i);
if (coeff.compareTo(BigDecimal.ZERO) == 0) {
continue;
}
// 符号处理
if (sb.length() > 4 && coeff.compareTo(BigDecimal.ZERO) > 0) {
sb.append(" + ");
} else if (sb.length() > 4 && coeff.compareTo(BigDecimal.ZERO) < 0) {
sb.append(" - ");
coeff = coeff.abs();
}
// 系数和变量
if (i == 0) {
// 常数项
sb.append(String.format("%.4f", coeff));
} else {
// 非常数项
if (coeff.compareTo(BigDecimal.ONE) != 0 && coeff.compareTo(BigDecimal.ONE.negate()) != 0) {
sb.append(String.format("%.4f", coeff));
}
sb.append("x");
if (i > 1) {
sb.append("^").append(i);
}
}
}
return sb.toString();
}
/**
* xy
*/
public BigDecimal predict(BigDecimal x) {
BigDecimal result = BigDecimal.ZERO;
BigDecimal xPower = BigDecimal.ONE;
for (int i = 0; i < coefficients.size(); i++) {
result = result.add(coefficients.get(i).multiply(xPower));
xPower = xPower.multiply(x);
}
return result;
}
@Override
public String toString() {
return String.format("%s", equationString);
}
}

View File

@ -0,0 +1,70 @@
package com.gunshi.project.hsz.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.gunshi.project.hsz.common.validate.markers.Insert;
import com.gunshi.project.hsz.common.validate.markers.Update;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* :
* author: xusan
* date: 2024-12-19 10:00:00
*/
@Schema(description = "数据检查规则表")
@Data
@TableName("public.sy_data_check_rule")
public class SyDataCheckRule implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
@Schema(description = "id")
@NotNull(message = "id不能为空", groups = {Update.class})
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
*
*/
@TableField(value = "dvcd")
@Schema(description = "测点编号")
@NotBlank(message = "测点编号不能为空", groups = {Insert.class, Update.class})
@Size(max = 255, message = "测点编号最大长度要小于 255")
private String dvcd;
/**
*
*/
@TableField(value = "rz")
@Schema(description = "库水位大于多少")
private BigDecimal rz;
/**
*
*/
@TableField(value = "sy_value")
@Schema(description = "渗压水位小于多少")
private BigDecimal syValue;
/**
* 0 1
*/
@TableField(value = "is_available")
@Schema(description = "是否启用 0否 1是")
private Integer isAvailable;
}

View File

@ -0,0 +1,91 @@
package com.gunshi.project.hsz.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.gunshi.core.dateformat.DateFormatString;
import com.gunshi.project.hsz.common.validate.markers.Insert;
import com.gunshi.project.hsz.common.validate.markers.Update;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* :
* author: xusan
* date: 2024-12-19 10:00:00
*/
@Schema(description = "回归方程数据表")
@Data
@TableName("public.sy_regression_data")
public class SyRegressionData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
@Schema(description = "id")
@NotNull(message = "id不能为空", groups = {Update.class})
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
*
*/
@TableField(value = "dvcd")
@Schema(description = "测点编号")
@NotBlank(message = "测点编号不能为空", groups = {Insert.class, Update.class})
@Size(max = 50, message = "测点编号最大长度要小于 50")
private String dvcd;
/**
*
*/
@TableField(value = "starttime")
@Schema(description = "开始时间")
@NotNull(message = "开始时间不能为空", groups = {Insert.class, Update.class})
@JsonFormat(pattern = DateFormatString.YYYY_MM_DD_HH_MM_SS, timezone = "GMT+8")
private Date startTime;
/**
*
*/
@TableField(value = "endtime")
@Schema(description = "结束时间")
@NotNull(message = "结束时间不能为空", groups = {Insert.class, Update.class})
@JsonFormat(pattern = DateFormatString.YYYY_MM_DD_HH_MM_SS, timezone = "GMT+8")
private Date endTime;
/**
*
*/
@TableField(value = "regressionequation")
@Schema(description = "回归方程")
private String regressionEquation;
/**
* -
*/
@TableField(value = "_order")
@Schema(description = "排序(一阶-四阶)")
private Integer order;
/**
*
*/
@TableField(value = "create_time")
@Schema(description = "创建时间")
@JsonFormat(pattern = DateFormatString.YYYY_MM_DD_HH_MM_SS, timezone = "GMT+8")
private Date createTime;
}

View File

@ -11,6 +11,7 @@ import com.gunshi.model.vo.FloodAlgorithemVo;
import com.gunshi.project.hsz.common.model.StPptnR; import com.gunshi.project.hsz.common.model.StPptnR;
import com.gunshi.project.hsz.common.model.StRsvrR; import com.gunshi.project.hsz.common.model.StRsvrR;
import com.gunshi.project.hsz.common.model.StStbprpB; import com.gunshi.project.hsz.common.model.StStbprpB;
import com.gunshi.project.hsz.common.model.vo.OsmoticPressDetailVo;
import com.gunshi.project.hsz.entity.vo.ForeRainStatVo; import com.gunshi.project.hsz.entity.vo.ForeRainStatVo;
import com.gunshi.project.hsz.entity.vo.ForeRainTimeVo; import com.gunshi.project.hsz.entity.vo.ForeRainTimeVo;
import com.gunshi.project.hsz.entity.vo.ForecastResultVo; import com.gunshi.project.hsz.entity.vo.ForecastResultVo;
@ -24,6 +25,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Instant; import java.time.Instant;
@ -1137,4 +1139,195 @@ public class ForecastResultsService extends ServiceImpl<ForecastResultsMapper, F
} }
return maps; return maps;
} }
public List<OsmoticPressDetailVo> calculateY(SyRegressionData syRegressionData, List<ForecastResultVo> voList) {
//过滤出预测时间的数据
List<ForecastResultVo> collect = voList.stream().filter(o -> {
return o.getIspreDrp().equals("1");
}).collect(Collectors.toList());
//获取方程组
String regressionEquation = syRegressionData.getRegressionEquation();
List<OsmoticPressDetailVo> res = new ArrayList<>();
//进行计算
for (ForecastResultVo forecastResultVo : collect) {
// 获取预测水位值作为x
BigDecimal x = forecastResultVo.getYcSwHValue();
if (x == null) {
continue; // 如果x为空跳过计算
}
// 根据回归方程计算y值
BigDecimal y = calculateRegressionValue(regressionEquation, x);
// 构建返回结果
OsmoticPressDetailVo detailVo = new OsmoticPressDetailVo();
detailVo.setTm(forecastResultVo.getTm()); // 监测时间
detailVo.setValue(y); // 计算得到的监测值
detailVo.setRz(x); // 库水位(使用预测水位值)
res.add(detailVo);
}
// 按时间升序排列
res.sort((o1, o2) -> {
if (o1.getTm() == null && o2.getTm() == null) {
return 0;
}
if (o1.getTm() == null) {
return -1;
}
if (o2.getTm() == null) {
return 1;
}
return o1.getTm().compareTo(o2.getTm());
});
return res;
}
/**
* y
* @param equation
* @param x x
* @return y
*/
private BigDecimal calculateRegressionValue(String equation, BigDecimal x) {
if (equation == null || equation.trim().isEmpty()) {
return BigDecimal.ZERO;
}
// 清理方程字符串移除空格和y=
String cleanEquation = equation.replace("y =", "").replace(" ", "").toLowerCase();
try {
BigDecimal result;
// 根据方程的形式判断阶数并计算
if (cleanEquation.contains("x^4")) {
result = calculateFourthOrder(cleanEquation, x);
} else if (cleanEquation.contains("x^3")) {
result = calculateThirdOrder(cleanEquation, x);
} else if (cleanEquation.contains("x^2")) {
result = calculateSecondOrder(cleanEquation, x);
} else {
result = calculateFirstOrder(cleanEquation, x);
}
// 四舍五入保留两位小数
return result.setScale(2, RoundingMode.HALF_UP);
} catch (Exception e) {
// 计算异常时返回0
return BigDecimal.ZERO;
}
}
/**
* 线 y = ax + b
*/
private BigDecimal calculateFirstOrder(String equation, BigDecimal x) {
// 解析方程系数格式如0.0455x+94.2395
String[] parts = equation.split("x");
if (parts.length < 2) {
return BigDecimal.ZERO;
}
BigDecimal a = parseCoefficient(parts[0]);
BigDecimal b = parseCoefficient(parts[1]);
return a.multiply(x).add(b);
}
/**
* y = ax^2 + bx + c
*/
private BigDecimal calculateSecondOrder(String equation, BigDecimal x) {
// 解析方程系数,格式如:-68.4211x^2+16312.8684x-972224.1397
String[] parts = equation.split("x");
if (parts.length < 3) {
return BigDecimal.ZERO;
}
BigDecimal a = parseCoefficient(parts[0]);
BigDecimal b = parseCoefficient(parts[1].replace("^2", ""));
BigDecimal c = parseCoefficient(parts[2]);
BigDecimal x2 = x.multiply(x); // x^2
return a.multiply(x2).add(b.multiply(x)).add(c);
}
/**
* y = ax^3 + bx^2 + cx + d
*/
private BigDecimal calculateThirdOrder(String equation, BigDecimal x) {
// 解析方程系数,格式如:-2291.6667x^3+819497.9167x^2-97683901.8750x+3881297151.1650
String[] parts = equation.split("x");
if (parts.length < 4) {
return BigDecimal.ZERO;
}
BigDecimal a = parseCoefficient(parts[0]);
BigDecimal b = parseCoefficient(parts[1].replace("^3", "").replace("^2", ""));
BigDecimal c = parseCoefficient(parts[2].replace("^2", ""));
BigDecimal d = parseCoefficient(parts[3]);
BigDecimal x2 = x.multiply(x); // x^2
BigDecimal x3 = x2.multiply(x); // x^3
return a.multiply(x3).add(b.multiply(x2)).add(c.multiply(x)).add(d);
}
/**
* y = ax^4 + bx^3 + cx^2 + dx + e
*/
private BigDecimal calculateFourthOrder(String equation, BigDecimal x) {
// 解析方程系数,格式如:-5.9039x^4+523.5482x^3+316095.2736x^2-57676816.2672x+2688986002.6804
String[] parts = equation.split("x");
if (parts.length < 5) {
return BigDecimal.ZERO;
}
BigDecimal a = parseCoefficient(parts[0]);
BigDecimal b = parseCoefficient(parts[1].replace("^4", "").replace("^3", ""));
BigDecimal c = parseCoefficient(parts[2].replace("^3", "").replace("^2", ""));
BigDecimal d = parseCoefficient(parts[3].replace("^2", ""));
BigDecimal e = parseCoefficient(parts[4]);
BigDecimal x2 = x.multiply(x); // x^2
BigDecimal x3 = x2.multiply(x); // x^3
BigDecimal x4 = x3.multiply(x); // x^4
return a.multiply(x4).add(b.multiply(x3)).add(c.multiply(x2)).add(d.multiply(x)).add(e);
}
/**
*
*/
private BigDecimal parseCoefficient(String coeffStr) {
if (coeffStr == null || coeffStr.isEmpty()) {
return BigDecimal.ZERO;
}
// 处理空字符串情况
if (coeffStr.equals("+") || coeffStr.equals("-")) {
coeffStr += "1";
}
// 处理没有显式数字的情况x^2+x+1 中的第一个x系数为1
if (coeffStr.equals("")) {
return BigDecimal.ONE;
}
try {
return new BigDecimal(coeffStr);
} catch (NumberFormatException e) {
// 如果解析失败,尝试处理特殊情况
if (coeffStr.equals("+")) {
return BigDecimal.ONE;
} else if (coeffStr.equals("-")) {
return BigDecimal.ONE.negate();
}
return BigDecimal.ZERO;
}
}
} }

View File

@ -13,11 +13,15 @@ import com.gunshi.project.hsz.entity.dto.ArtificialJcskSyDeleteDto;
import com.gunshi.project.hsz.common.model.so.JcskSyRPageSo; import com.gunshi.project.hsz.common.model.so.JcskSyRPageSo;
import com.gunshi.project.hsz.common.model.so.OsmoticDetailQuerySo; import com.gunshi.project.hsz.common.model.so.OsmoticDetailQuerySo;
import com.gunshi.project.hsz.common.model.so.OsmoticQuerySo; import com.gunshi.project.hsz.common.model.so.OsmoticQuerySo;
import com.gunshi.project.hsz.entity.dto.ProjectSafeCalculateDto;
import com.gunshi.project.hsz.entity.vo.*; import com.gunshi.project.hsz.entity.vo.*;
import com.gunshi.project.hsz.common.mapper.JcskSlBMapper; import com.gunshi.project.hsz.common.mapper.JcskSlBMapper;
import com.gunshi.project.hsz.mapper.JcskSyREightAmMapper; import com.gunshi.project.hsz.mapper.JcskSyREightAmMapper;
import com.gunshi.project.hsz.common.mapper.JcskSyRMapper; import com.gunshi.project.hsz.common.mapper.JcskSyRMapper;
import com.gunshi.project.hsz.common.model.JcskSyB; import com.gunshi.project.hsz.common.model.JcskSyB;
import com.gunshi.project.hsz.model.AttResBase;
import com.gunshi.project.hsz.model.JcskSyREightAm;
import com.gunshi.project.hsz.model.RegressionEquation;
import com.gunshi.project.hsz.util.*; import com.gunshi.project.hsz.util.*;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
@ -30,6 +34,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
@ -776,4 +781,98 @@ public class JcskSyRService extends ServiceImpl<JcskSyRMapper, JcskSyR> {
int delete = this.baseMapper.delete(queryWrapper); int delete = this.baseMapper.delete(queryWrapper);
return delete > 0; return delete > 0;
} }
@Autowired
private AttResBaseService attResBaseService;
/**
*
* @param dto
* @return
*/
public Map<String, RegressionEquation> calculate(ProjectSafeCalculateDto dto) {
LambdaQueryWrapper<JcskSyB> queryWrapper = new LambdaQueryWrapper<>();
Map<String, RegressionEquation> res = new HashMap<>();
//根据dvcd查询stcd和mpcd
queryWrapper.eq(JcskSyB::getDvcd, dto.getDvcd());
JcskSyB jcskSyB = jcskSyBService.getBaseMapper().selectOne(queryWrapper);
String stcd = jcskSyB.getStcd();
String mpcd = jcskSyB.getMpcd();
//取得这个时间段得所有八点得数据
LambdaQueryWrapper<JcskSyREightAm> eightAmLambdaQueryWrapper = new LambdaQueryWrapper<>();
eightAmLambdaQueryWrapper.eq(JcskSyREightAm::getStcd, stcd);
eightAmLambdaQueryWrapper.eq(JcskSyREightAm::getMpcd, mpcd);
eightAmLambdaQueryWrapper.ge(JcskSyREightAm::getMstm,dto.getDateTimeRangeSo().getStart());
eightAmLambdaQueryWrapper.le(JcskSyREightAm::getMstm,dto.getDateTimeRangeSo().getEnd());
eightAmLambdaQueryWrapper.orderByAsc(JcskSyREightAm::getMstm);
List<JcskSyREightAm> jcskSyREightAms = jcskSyREightAmMapper.selectList(eightAmLambdaQueryWrapper);
AttResBase attResBase = attResBaseService.list().get(0);
OsmoticQuerySo querySo = new OsmoticQuerySo();
querySo.setDateTimeRangeSo(dto.getDateTimeRangeSo());
List<StRzVo> stRzVos = baseMapper.qeury8AmRz(querySo,attResBase.getStcd());
Map<String, BigDecimal> rzMap = stRzVos.stream()
.collect(Collectors.toMap(
StRzVo::getTm, // key: 时间
StRzVo::getRz, // value: 水位
(existing, replacement) -> existing // 如果key冲突保留已存在的
));
//组合成为 时间 库水位 管道水位的数据
List<OsmoticPressDetailVo> data = new ArrayList<>();
for (JcskSyREightAm eightAm : jcskSyREightAms) {
OsmoticPressDetailVo detailVo = new OsmoticPressDetailVo();
// 设置监测时间和监测值(管道水位)
String mstmStr = eightAm.getMstm().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
detailVo.setTm(mstmStr);
detailVo.setValue(eightAm.getSpprwl());
// 查找对应的库水位
BigDecimal rzValue = findClosestRzValue(rzMap, mstmStr);
detailVo.setRz(rzValue);
data.add(detailVo);
}
if(data.isEmpty()){
return res;
}
RegressionEquation first = RegressionAnalysis.calculateLinear(data);
RegressionEquation second = RegressionAnalysis.calculateQuadratic(data);
RegressionEquation three = RegressionAnalysis.calculateCubic(data);
RegressionEquation four = RegressionAnalysis.calculateQuartic(data);
res.put("first", first);
res.put("second", second);
res.put("three", three);
res.put("four", four);
return res;
}
/**
*
*/
private BigDecimal findClosestRzValue(Map<String, BigDecimal> rzMap, String targetTimeStr) {
// 如果直接匹配到,直接返回
if (rzMap.containsKey(targetTimeStr)) {
return rzMap.get(targetTimeStr);
}
// 如果没有直接匹配,查找最接近的时间
LocalDateTime targetTime = LocalDateTime.parse(targetTimeStr,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
String closestTime = null;
long minDiff = Long.MAX_VALUE;
for (String timeStr : rzMap.keySet()) {
LocalDateTime time = LocalDateTime.parse(timeStr,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
long diff = Math.abs(Duration.between(targetTime, time).toMinutes());
if (diff < minDiff) {
minDiff = diff;
closestTime = timeStr;
}
}
return closestTime != null ? rzMap.get(closestTime) : BigDecimal.ZERO;
}
} }

View File

@ -0,0 +1,54 @@
package com.gunshi.project.hsz.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gunshi.project.hsz.common.model.JcskSyB;
import com.gunshi.project.hsz.entity.so.SyDataCheckRulePageSo;
import com.gunshi.project.hsz.mapper.StZvarlBMapper;
import com.gunshi.project.hsz.mapper.SyDataCheckRuleMapper;
import com.gunshi.project.hsz.model.StZvarlB;
import com.gunshi.project.hsz.model.SyDataCheckRule;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class SyDataCheckRuleService extends ServiceImpl<SyDataCheckRuleMapper, SyDataCheckRule> {
public Page<SyDataCheckRule> pageQuery(SyDataCheckRulePageSo page) {
LambdaQueryWrapper<SyDataCheckRule> queryWrapper = new LambdaQueryWrapper<>();
if(!StringUtils.isBlank(page.getDvcd())){
queryWrapper.eq(SyDataCheckRule::getDvcd,page.getDvcd());
}
Page<SyDataCheckRule> syDataCheckRulePage = this.baseMapper.selectPage(page.getPageSo().toPage(), queryWrapper);
return syDataCheckRulePage;
}
public SyDataCheckRule saveData(SyDataCheckRule dto) {
LambdaQueryWrapper<SyDataCheckRule> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SyDataCheckRule::getDvcd,dto.getDvcd());
SyDataCheckRule syDataCheckRule = this.baseMapper.selectOne(queryWrapper);
if(syDataCheckRule != null){
throw new IllegalArgumentException("对不起,该测点已配置清洗策略");
}
save(dto);
return dto;
}
public Boolean updateData(SyDataCheckRule dto) {
LambdaQueryWrapper<SyDataCheckRule> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SyDataCheckRule::getDvcd,dto.getDvcd());
queryWrapper.ne(SyDataCheckRule::getId,dto.getId());
SyDataCheckRule syDataCheckRule = this.baseMapper.selectOne(queryWrapper);
if(syDataCheckRule != null){
throw new IllegalArgumentException("对不起,该测点已配置清洗策略");
}
boolean flag = updateById(dto);
return flag;
}
}

View File

@ -0,0 +1,33 @@
package com.gunshi.project.hsz.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gunshi.project.hsz.mapper.StZvarlBMapper;
import com.gunshi.project.hsz.mapper.SyRegressionDataMapper;
import com.gunshi.project.hsz.model.StZvarlB;
import com.gunshi.project.hsz.model.SyRegressionData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class SyRegressionDataService extends ServiceImpl<SyRegressionDataMapper, SyRegressionData> {
public List<SyRegressionData> queryData(SyRegressionData dto) {
LambdaQueryWrapper<SyRegressionData> queryWrapper = new LambdaQueryWrapper<>();
if(dto.getDvcd() != null){
queryWrapper.eq(SyRegressionData::getDvcd, dto.getDvcd());
}
if(dto.getOrder() != null){
queryWrapper.eq(SyRegressionData::getOrder, dto.getOrder());
}
List<SyRegressionData> syRegressionData = this.baseMapper.selectList(queryWrapper);
return syRegressionData;
}
}

View File

@ -0,0 +1,204 @@
package com.gunshi.project.hsz.util;
import com.gunshi.project.hsz.common.model.vo.OsmoticPressDetailVo;
import com.gunshi.project.hsz.model.RegressionEquation;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.stream.Collectors;
/**
* -线
*/
public class ProjectCalculateUtil {
private static List<OsmoticPressDetailVo> filterValidData(List<OsmoticPressDetailVo> data) {
return data.stream()
.filter(vo -> vo.getRz() != null && vo.getValue() != null)
.collect(Collectors.toList());
}
private static BigDecimal calculateRSquared(List<OsmoticPressDetailVo> data,
List<BigDecimal> coefficients,
BigDecimal meanY) {
BigDecimal ssTot = BigDecimal.ZERO;
BigDecimal ssRes = BigDecimal.ZERO;
for (OsmoticPressDetailVo vo : data) {
BigDecimal x = vo.getRz();
BigDecimal y = vo.getValue();
BigDecimal yPredicted = predictWithCoefficients(x, coefficients);
BigDecimal diffActual = y.subtract(meanY);
ssTot = ssTot.add(diffActual.multiply(diffActual));
BigDecimal diffResidual = y.subtract(yPredicted);
ssRes = ssRes.add(diffResidual.multiply(diffResidual));
}
if (ssTot.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ONE;
}
return BigDecimal.ONE.subtract(ssRes.divide(ssTot, 30, RoundingMode.HALF_UP));
}
private static BigDecimal predictWithCoefficients(BigDecimal x, List<BigDecimal> coefficients) {
BigDecimal result = BigDecimal.ZERO;
BigDecimal xPower = BigDecimal.ONE;
for (BigDecimal coeff : coefficients) {
result = result.add(coeff.multiply(xPower));
xPower = xPower.multiply(x);
}
return result;
}
private static BigDecimal calculateDeterminant3x3(BigDecimal a11, BigDecimal a12, BigDecimal a13,
BigDecimal a21, BigDecimal a22, BigDecimal a23,
BigDecimal a31, BigDecimal a32, BigDecimal a33) {
return a11.multiply(a22.multiply(a33).subtract(a23.multiply(a32)))
.subtract(a12.multiply(a21.multiply(a33).subtract(a23.multiply(a31))))
.add(a13.multiply(a21.multiply(a32).subtract(a22.multiply(a31))));
}
/**
* 线1 xy
*/
public static RegressionEquation calculateLinear(List<OsmoticPressDetailVo> data) {
List<OsmoticPressDetailVo> validData = filterValidData(data);
if (validData.isEmpty()) {
throw new IllegalArgumentException("没有有效的数据点进行计算");
}
int n = validData.size();
BigDecimal sumX = BigDecimal.ZERO;
BigDecimal sumY = BigDecimal.ZERO;
BigDecimal sumXY = BigDecimal.ZERO;
BigDecimal sumX2 = BigDecimal.ZERO;
BigDecimal sumY2 = BigDecimal.ZERO;
for (OsmoticPressDetailVo vo : validData) {
BigDecimal x = vo.getRz();
BigDecimal y = vo.getValue();
sumX = sumX.add(x);
sumY = sumY.add(y);
sumXY = sumXY.add(x.multiply(y));
sumX2 = sumX2.add(x.multiply(x));
sumY2 = sumY2.add(y.multiply(y));
}
BigDecimal nBig = new BigDecimal(n);
BigDecimal meanX = sumX.divide(nBig, 30, RoundingMode.HALF_UP);
BigDecimal meanY = sumY.divide(nBig, 30, RoundingMode.HALF_UP);
BigDecimal numerator = sumXY.subtract(nBig.multiply(meanX).multiply(meanY));
BigDecimal denominator = sumX2.subtract(nBig.multiply(meanX).multiply(meanX));
if (denominator.compareTo(BigDecimal.ZERO) == 0) {
throw new IllegalArgumentException("数据点x值相同无法计算斜率");
}
BigDecimal slope = numerator.divide(denominator, 30, RoundingMode.HALF_UP);
BigDecimal intercept = meanY.subtract(slope.multiply(meanX));
// 计算R²
BigDecimal rSquared = calculateRSquared(validData,
List.of(intercept, slope), meanY);
// 系数列表:[常数项, 一次项系数]
List<BigDecimal> coefficients = List.of(
intercept.setScale(10, RoundingMode.HALF_UP),
slope.setScale(10, RoundingMode.HALF_UP)
);
return new RegressionEquation(1, coefficients, rSquared, n);
}
/**
* 2
*/
public static RegressionEquation calculateQuadratic(List<OsmoticPressDetailVo> data) {
List<OsmoticPressDetailVo> validData = filterValidData(data);
if (validData.size() < 3) {
throw new IllegalArgumentException("二次回归至少需要3个数据点");
}
int n = validData.size();
BigDecimal sumX = BigDecimal.ZERO, sumY = BigDecimal.ZERO;
BigDecimal sumX2 = BigDecimal.ZERO, sumX3 = BigDecimal.ZERO, sumX4 = BigDecimal.ZERO;
BigDecimal sumXY = BigDecimal.ZERO, sumX2Y = BigDecimal.ZERO;
for (OsmoticPressDetailVo vo : validData) {
BigDecimal x = vo.getRz();
BigDecimal y = vo.getValue();
BigDecimal x2 = x.multiply(x);
BigDecimal x3 = x2.multiply(x);
BigDecimal x4 = x3.multiply(x);
sumX = sumX.add(x);
sumY = sumY.add(y);
sumX2 = sumX2.add(x2);
sumX3 = sumX3.add(x3);
sumX4 = sumX4.add(x4);
sumXY = sumXY.add(x.multiply(y));
sumX2Y = sumX2Y.add(x2.multiply(y));
}
BigDecimal nBig = new BigDecimal(n);
// 使用克莱姆法则求解
BigDecimal det = calculateDeterminant3x3(
nBig, sumX, sumX2,
sumX, sumX2, sumX3,
sumX2, sumX3, sumX4
);
if (det.compareTo(BigDecimal.ZERO) == 0) {
throw new IllegalArgumentException("矩阵奇异,无法求解二次回归方程");
}
BigDecimal detA0 = calculateDeterminant3x3(
sumY, sumX, sumX2,
sumXY, sumX2, sumX3,
sumX2Y, sumX3, sumX4
);
BigDecimal detA1 = calculateDeterminant3x3(
nBig, sumY, sumX2,
sumX, sumXY, sumX3,
sumX2, sumX2Y, sumX4
);
BigDecimal detA2 = calculateDeterminant3x3(
nBig, sumX, sumY,
sumX, sumX2, sumXY,
sumX2, sumX3, sumX2Y
);
BigDecimal a0 = detA0.divide(det, 30, RoundingMode.HALF_UP);
BigDecimal a1 = detA1.divide(det, 30, RoundingMode.HALF_UP);
BigDecimal a2 = detA2.divide(det, 30, RoundingMode.HALF_UP);
// 计算R²
BigDecimal meanY = sumY.divide(nBig, 30, RoundingMode.HALF_UP);
BigDecimal rSquared = calculateRSquared(validData,
List.of(a0, a1, a2), meanY);
// 系数列表:[常数项, 一次项系数, 二次项系数]
List<BigDecimal> coefficients = List.of(
a0.setScale(10, RoundingMode.HALF_UP),
a1.setScale(10, RoundingMode.HALF_UP),
a2.setScale(10, RoundingMode.HALF_UP)
);
return new RegressionEquation(2, coefficients, rSquared, n);
}
}

View File

@ -0,0 +1,581 @@
package com.gunshi.project.hsz.util;
import com.gunshi.project.hsz.common.model.vo.OsmoticPressDetailVo;
import com.gunshi.project.hsz.model.RegressionEquation;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
*
*/
public class RegressionAnalysis {
/**
* 线1
*/
public static RegressionEquation calculateLinear(List<OsmoticPressDetailVo> data) {
int n = 0;
BigDecimal rSquared = null;
List<BigDecimal> coefficients = null;
try {
List<OsmoticPressDetailVo> validData = filterValidData(data);
if (validData.isEmpty()) {
throw new IllegalArgumentException("没有有效的数据点进行计算");
}
n = validData.size();
BigDecimal sumX = BigDecimal.ZERO;
BigDecimal sumY = BigDecimal.ZERO;
BigDecimal sumXY = BigDecimal.ZERO;
BigDecimal sumX2 = BigDecimal.ZERO;
BigDecimal sumY2 = BigDecimal.ZERO;
for (OsmoticPressDetailVo vo : validData) {
BigDecimal x = vo.getRz();
BigDecimal y = vo.getValue();
sumX = sumX.add(x);
sumY = sumY.add(y);
sumXY = sumXY.add(x.multiply(y));
sumX2 = sumX2.add(x.multiply(x));
sumY2 = sumY2.add(y.multiply(y));
}
BigDecimal nBig = new BigDecimal(n);
BigDecimal meanX = sumX.divide(nBig, 100, RoundingMode.HALF_UP);
BigDecimal meanY = sumY.divide(nBig, 100, RoundingMode.HALF_UP);
BigDecimal numerator = sumXY.subtract(nBig.multiply(meanX).multiply(meanY));
BigDecimal denominator = sumX2.subtract(nBig.multiply(meanX).multiply(meanX));
if (denominator.compareTo(BigDecimal.ZERO) == 0) {
throw new IllegalArgumentException("数据点x值相同无法计算斜率");
}
BigDecimal slope = numerator.divide(denominator, 100, RoundingMode.HALF_UP);
BigDecimal intercept = meanY.subtract(slope.multiply(meanX));
// 计算R²
rSquared = calculateRSquared(validData, List.of(intercept, slope), meanY);
// 系数列表:[常数项, 一次项系数] - 不进行四舍五入
coefficients = List.of(intercept, slope);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
}
return new RegressionEquation(1, coefficients, rSquared, n);
}
/**
* 2
*/
public static RegressionEquation calculateQuadratic(List<OsmoticPressDetailVo> data) {
int n = 0;
BigDecimal rSquared = null;
List<BigDecimal> coefficients = null;
try {
List<OsmoticPressDetailVo> validData = filterValidData(data);
if (validData.size() < 3) {
throw new IllegalArgumentException("二次回归至少需要3个数据点");
}
n = validData.size();
// 计算各项和
BigDecimal sumX = BigDecimal.ZERO, sumY = BigDecimal.ZERO;
BigDecimal sumX2 = BigDecimal.ZERO, sumX3 = BigDecimal.ZERO, sumX4 = BigDecimal.ZERO;
BigDecimal sumXY = BigDecimal.ZERO, sumX2Y = BigDecimal.ZERO;
for (OsmoticPressDetailVo vo : validData) {
BigDecimal x = vo.getRz();
BigDecimal y = vo.getValue();
BigDecimal x2 = x.multiply(x);
BigDecimal x3 = x2.multiply(x);
BigDecimal x4 = x3.multiply(x);
sumX = sumX.add(x);
sumY = sumY.add(y);
sumX2 = sumX2.add(x2);
sumX3 = sumX3.add(x3);
sumX4 = sumX4.add(x4);
sumXY = sumXY.add(x.multiply(y));
sumX2Y = sumX2Y.add(x2.multiply(y));
}
BigDecimal nBig = new BigDecimal(n);
// 使用克莱姆法则求解
BigDecimal det = calculateDeterminant3x3(
nBig, sumX, sumX2,
sumX, sumX2, sumX3,
sumX2, sumX3, sumX4
);
if (det.compareTo(BigDecimal.ZERO) == 0) {
throw new IllegalArgumentException("矩阵奇异,无法求解二次回归方程");
}
// 求解常数项 a
BigDecimal detA = calculateDeterminant3x3(
sumY, sumX, sumX2,
sumXY, sumX2, sumX3,
sumX2Y, sumX3, sumX4
);
// 求解一次项系数 b
BigDecimal detB = calculateDeterminant3x3(
nBig, sumY, sumX2,
sumX, sumXY, sumX3,
sumX2, sumX2Y, sumX4
);
// 求解二次项系数 c
BigDecimal detC = calculateDeterminant3x3(
nBig, sumX, sumY,
sumX, sumX2, sumXY,
sumX2, sumX3, sumX2Y
);
BigDecimal a = detA.divide(det, 100, RoundingMode.HALF_UP); // 常数项
BigDecimal b = detB.divide(det, 100, RoundingMode.HALF_UP); // 一次项系数
BigDecimal c = detC.divide(det, 100, RoundingMode.HALF_UP); // 二次项系数
// 计算R²
BigDecimal meanY = sumY.divide(nBig, 100, RoundingMode.HALF_UP);
rSquared = calculateRSquared(validData, List.of(a, b, c), meanY);
// 系数列表:[常数项, 一次项系数, 二次项系数] - 不进行四舍五入
coefficients = List.of(a, b, c);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
}
return new RegressionEquation(2, coefficients, rSquared, n);
}
/**
* 3-
*/
public static RegressionEquation calculateCubic(List<OsmoticPressDetailVo> data) {
int n = 0;
List<BigDecimal> coefficientsList = null;
BigDecimal rSquared = null;
try {
List<OsmoticPressDetailVo> validData = filterValidData(data);
if (validData.size() < 4) {
throw new IllegalArgumentException("三次回归至少需要4个数据点");
}
n = validData.size();
// 使用中心化数据提高数值稳定性
BigDecimal meanX = calculateMean(validData, OsmoticPressDetailVo::getRz);
// 计算各项和(使用中心化数据)
BigDecimal sumX = BigDecimal.ZERO, sumY = BigDecimal.ZERO;
BigDecimal sumX2 = BigDecimal.ZERO, sumX3 = BigDecimal.ZERO, sumX4 = BigDecimal.ZERO;
BigDecimal sumX5 = BigDecimal.ZERO, sumX6 = BigDecimal.ZERO;
BigDecimal sumXY = BigDecimal.ZERO, sumX2Y = BigDecimal.ZERO, sumX3Y = BigDecimal.ZERO;
for (OsmoticPressDetailVo vo : validData) {
BigDecimal x = vo.getRz().subtract(meanX); // 中心化
BigDecimal y = vo.getValue();
BigDecimal x2 = x.multiply(x);
BigDecimal x3 = x2.multiply(x);
BigDecimal x4 = x3.multiply(x);
BigDecimal x5 = x4.multiply(x);
BigDecimal x6 = x5.multiply(x);
sumX = sumX.add(x);
sumY = sumY.add(y);
sumX2 = sumX2.add(x2);
sumX3 = sumX3.add(x3);
sumX4 = sumX4.add(x4);
sumX5 = sumX5.add(x5);
sumX6 = sumX6.add(x6);
sumXY = sumXY.add(x.multiply(y));
sumX2Y = sumX2Y.add(x2.multiply(y));
sumX3Y = sumX3Y.add(x3.multiply(y));
}
BigDecimal nBig = new BigDecimal(n);
// 构建正规方程组(中心化后的矩阵条件数更好)
BigDecimal[][] matrix = {
{nBig, sumX, sumX2, sumX3},
{sumX, sumX2, sumX3, sumX4},
{sumX2, sumX3, sumX4, sumX5},
{sumX3, sumX4, sumX5, sumX6}
};
BigDecimal[] vector = {sumY, sumXY, sumX2Y, sumX3Y};
// 使用改进的高斯消元法
BigDecimal[] centeredCoefficients = solveLinearSystemImproved(matrix, vector);
if (centeredCoefficients == null) {
throw new IllegalArgumentException("无法求解三次回归方程");
}
// 将中心化系数转换回原始坐标
BigDecimal[] originalCoefficients = convertToOriginalCoefficients(centeredCoefficients, meanX);
// 计算R²
BigDecimal meanY = sumY.divide(nBig, MathContext.DECIMAL128);
coefficientsList = Arrays.asList(originalCoefficients);
rSquared = calculateRSquared(validData, coefficientsList, meanY);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
}
return new RegressionEquation(3, coefficientsList, rSquared, n);
}
/**
*
*/
private static BigDecimal[] convertToOriginalCoefficients(BigDecimal[] centeredCoeffs, BigDecimal meanX) {
// 对于三次多项式y = a + b(x - μ) + c(x - μ)² + d(x - μ)³
// 展开后y = (a - bμ + cμ² - dμ³) + (b - 2cμ + 3dμ²)x + (c - 3dμ)x² + d x³
BigDecimal a = centeredCoeffs[0];
BigDecimal b = centeredCoeffs[1];
BigDecimal c = centeredCoeffs[2];
BigDecimal d = centeredCoeffs[3];
BigDecimal mu = meanX;
BigDecimal mu2 = mu.multiply(mu);
BigDecimal mu3 = mu2.multiply(mu);
BigDecimal newA = a.subtract(b.multiply(mu))
.add(c.multiply(mu2))
.subtract(d.multiply(mu3));
BigDecimal newB = b.subtract(BigDecimal.valueOf(2).multiply(c).multiply(mu))
.add(BigDecimal.valueOf(3).multiply(d).multiply(mu2));
BigDecimal newC = c.subtract(BigDecimal.valueOf(3).multiply(d).multiply(mu));
BigDecimal newD = d;
return new BigDecimal[]{newA, newB, newC, newD};
}
/**
*
*/
private static BigDecimal[] solveLinearSystemWithPivot(BigDecimal[][] matrix, BigDecimal[] vector) {
int n = vector.length;
BigDecimal[][] a = new BigDecimal[n][n];
BigDecimal[] b = new BigDecimal[n];
for (int i = 0; i < n; i++) {
System.arraycopy(matrix[i], 0, a[i], 0, n);
b[i] = vector[i];
}
int[] rowPerm = new int[n];
int[] colPerm = new int[n];
for (int i = 0; i < n; i++) {
rowPerm[i] = i;
colPerm[i] = i;
}
// 完全主元高斯消元
for (int k = 0; k < n; k++) {
// 寻找主元
int maxRow = k, maxCol = k;
BigDecimal maxVal = a[rowPerm[k]][colPerm[k]].abs();
for (int i = k; i < n; i++) {
for (int j = k; j < n; j++) {
BigDecimal current = a[rowPerm[i]][colPerm[j]].abs();
if (current.compareTo(maxVal) > 0) {
maxVal = current;
maxRow = i;
maxCol = j;
}
}
}
// 交换行
if (maxRow != k) {
int temp = rowPerm[k];
rowPerm[k] = rowPerm[maxRow];
rowPerm[maxRow] = temp;
}
// 交换列
if (maxCol != k) {
int temp = colPerm[k];
colPerm[k] = colPerm[maxCol];
colPerm[maxCol] = temp;
}
// 主元为0矩阵奇异
if (a[rowPerm[k]][colPerm[k]].compareTo(BigDecimal.ZERO) == 0) {
return null;
}
// 消元
for (int i = k + 1; i < n; i++) {
BigDecimal factor = a[rowPerm[i]][colPerm[k]].divide(a[rowPerm[k]][colPerm[k]], 100, RoundingMode.HALF_UP);
for (int j = k; j < n; j++) {
a[rowPerm[i]][colPerm[j]] = a[rowPerm[i]][colPerm[j]].subtract(
factor.multiply(a[rowPerm[k]][colPerm[j]]));
}
b[rowPerm[i]] = b[rowPerm[i]].subtract(factor.multiply(b[rowPerm[k]]));
}
}
// 回代
BigDecimal[] x = new BigDecimal[n];
for (int i = n - 1; i >= 0; i--) {
BigDecimal sum = BigDecimal.ZERO;
for (int j = i + 1; j < n; j++) {
sum = sum.add(a[rowPerm[i]][colPerm[j]].multiply(x[colPerm[j]]));
}
x[colPerm[i]] = b[rowPerm[i]].subtract(sum).divide(a[rowPerm[i]][colPerm[i]], 100, RoundingMode.HALF_UP);
}
// 恢复原始顺序
BigDecimal[] result = new BigDecimal[n];
for (int i = 0; i < n; i++) {
result[i] = x[i];
}
return result;
}
// 其他辅助方法
private static List<OsmoticPressDetailVo> filterValidData(List<OsmoticPressDetailVo> data) {
return data.stream()
.filter(vo -> vo.getRz() != null && vo.getValue() != null)
.collect(Collectors.toList());
}
private static BigDecimal calculateDeterminant3x3(BigDecimal a11, BigDecimal a12, BigDecimal a13,
BigDecimal a21, BigDecimal a22, BigDecimal a23,
BigDecimal a31, BigDecimal a32, BigDecimal a33) {
return a11.multiply(a22.multiply(a33).subtract(a23.multiply(a32)))
.subtract(a12.multiply(a21.multiply(a33).subtract(a23.multiply(a31))))
.add(a13.multiply(a21.multiply(a32).subtract(a22.multiply(a31))));
}
private static BigDecimal predictWithCoefficients(BigDecimal x, List<BigDecimal> coefficients) {
BigDecimal result = BigDecimal.ZERO;
BigDecimal xPower = BigDecimal.ONE;
for (BigDecimal coeff : coefficients) {
result = result.add(coeff.multiply(xPower));
xPower = xPower.multiply(x);
}
return result;
}
/**
* 4
*/
public static RegressionEquation calculateQuartic(List<OsmoticPressDetailVo> data) {
int n = 0;
List<BigDecimal> coeffsList = null;
BigDecimal rSquared = null;
try {
List<OsmoticPressDetailVo> validData = filterValidData(data);
if (validData.size() < 5) {
throw new IllegalArgumentException("四次回归至少需要5个数据点");
}
n = validData.size();
// 计算各项和sumX, sumY, sumX2, sumX3, sumX4, sumX5, sumX6, sumX7, sumX8, sumXY, sumX2Y, sumX3Y, sumX4Y
BigDecimal sumX = BigDecimal.ZERO, sumY = BigDecimal.ZERO;
BigDecimal sumX2 = BigDecimal.ZERO, sumX3 = BigDecimal.ZERO, sumX4 = BigDecimal.ZERO;
BigDecimal sumX5 = BigDecimal.ZERO, sumX6 = BigDecimal.ZERO, sumX7 = BigDecimal.ZERO, sumX8 = BigDecimal.ZERO;
BigDecimal sumXY = BigDecimal.ZERO, sumX2Y = BigDecimal.ZERO, sumX3Y = BigDecimal.ZERO, sumX4Y = BigDecimal.ZERO;
for (OsmoticPressDetailVo vo : validData) {
BigDecimal x = vo.getRz();
BigDecimal y = vo.getValue();
BigDecimal x2 = x.multiply(x);
BigDecimal x3 = x2.multiply(x);
BigDecimal x4 = x3.multiply(x);
BigDecimal x5 = x4.multiply(x);
BigDecimal x6 = x5.multiply(x);
BigDecimal x7 = x6.multiply(x);
BigDecimal x8 = x7.multiply(x);
sumX = sumX.add(x);
sumY = sumY.add(y);
sumX2 = sumX2.add(x2);
sumX3 = sumX3.add(x3);
sumX4 = sumX4.add(x4);
sumX5 = sumX5.add(x5);
sumX6 = sumX6.add(x6);
sumX7 = sumX7.add(x7);
sumX8 = sumX8.add(x8);
sumXY = sumXY.add(x.multiply(y));
sumX2Y = sumX2Y.add(x2.multiply(y));
sumX3Y = sumX3Y.add(x3.multiply(y));
sumX4Y = sumX4Y.add(x4.multiply(y));
}
BigDecimal nBig = new BigDecimal(n);
// 构建正规方程组的系数矩阵
BigDecimal[][] matrix = {
{nBig, sumX, sumX2, sumX3, sumX4}, // 第1行
{sumX, sumX2, sumX3, sumX4, sumX5}, // 第2行
{sumX2, sumX3, sumX4, sumX5, sumX6}, // 第3行
{sumX3, sumX4, sumX5, sumX6, sumX7}, // 第4行
{sumX4, sumX5, sumX6, sumX7, sumX8} // 第5行
};
// 构建右侧向量
BigDecimal[] vector = {sumY, sumXY, sumX2Y, sumX3Y, sumX4Y};
// 使用改进的高斯消元法求解系数 [a, b, c, d, e]
BigDecimal[] coefficients = solveLinearSystemImproved(matrix, vector);
if (coefficients == null) {
throw new IllegalArgumentException("无法求解四次回归方程(矩阵奇异)");
}
// 计算R²
BigDecimal meanY = sumY.divide(nBig, MathContext.DECIMAL128);
coeffsList = Arrays.asList(coefficients);
rSquared = calculateRSquared(validData, coeffsList, meanY);
} catch (IllegalArgumentException e) {
return null;
}
return new RegressionEquation(4, coeffsList, rSquared, n);
}
/**
* 线
* n Ax = b
*/
private static BigDecimal[] solveLinearSystemImproved(BigDecimal[][] matrix, BigDecimal[] vector) {
int n = vector.length;
// 复制矩阵和向量(避免修改原数据)
BigDecimal[][] a = new BigDecimal[n][n];
BigDecimal[] b = new BigDecimal[n];
for (int i = 0; i < n; i++) {
a[i] = matrix[i].clone();
b[i] = vector[i];
}
// 部分主元高斯消元:逐行处理,选择当前列的最大绝对值元素作为主元
for (int k = 0; k < n; k++) {
// 找到当前列k列中绝对值最大的行maxRow
int maxRow = k;
BigDecimal maxVal = a[k][k].abs(); // 初始为当前对角线元素
for (int i = k + 1; i < n; i++) {
BigDecimal current = a[i][k].abs();
if (current.compareTo(maxVal) > 0) {
maxVal = current;
maxRow = i;
}
}
// 交换行将最大主元所在行交换到当前行k
if (maxRow != k) {
BigDecimal[] tempRow = a[k];
a[k] = a[maxRow];
a[maxRow] = tempRow;
BigDecimal tempB = b[k];
b[k] = b[maxRow];
b[maxRow] = tempB;
}
// 检查主元是否为0矩阵奇异
if (a[k][k].compareTo(BigDecimal.ZERO) == 0) {
return null;
}
// 消元将第k列下方的元素消为0
for (int i = k + 1; i < n; i++) {
BigDecimal factor = a[i][k].divide(a[k][k], MathContext.DECIMAL128); // 除法保留高精度
for (int j = k; j < n; j++) {
a[i][j] = a[i][j].subtract(factor.multiply(a[k][j], MathContext.DECIMAL128), MathContext.DECIMAL128);
}
b[i] = b[i].subtract(factor.multiply(b[k], MathContext.DECIMAL128), MathContext.DECIMAL128);
}
}
// 回代:从最后一行开始,依次求解每个变量
BigDecimal[] x = new BigDecimal[n];
for (int i = n - 1; i >= 0; i--) {
BigDecimal sum = BigDecimal.ZERO;
for (int j = i + 1; j < n; j++) {
sum = sum.add(a[i][j].multiply(x[j], MathContext.DECIMAL128), MathContext.DECIMAL128);
}
x[i] = b[i].subtract(sum, MathContext.DECIMAL128)
.divide(a[i][i], MathContext.DECIMAL128); // 除法保留高精度
}
return x;
}
// ========== 辅助方法(已存在,无需修改) ==========
/**
*
*/
private static BigDecimal calculateMean(List<OsmoticPressDetailVo> data,
java.util.function.Function<OsmoticPressDetailVo, BigDecimal> extractor) {
BigDecimal sum = BigDecimal.ZERO;
for (OsmoticPressDetailVo vo : data) {
sum = sum.add(extractor.apply(vo));
}
return sum.divide(new BigDecimal(data.size()), MathContext.DECIMAL128);
}
/**
* R²
*/
private static BigDecimal calculateRSquared(List<OsmoticPressDetailVo> data,
List<BigDecimal> coefficients,
BigDecimal meanY) {
BigDecimal ssTot = BigDecimal.ZERO;
BigDecimal ssRes = BigDecimal.ZERO;
for (OsmoticPressDetailVo vo : data) {
BigDecimal x = vo.getRz();
BigDecimal y = vo.getValue();
BigDecimal yPredicted = predictWithCoefficients(x, coefficients);
BigDecimal diffActual = y.subtract(meanY);
ssTot = ssTot.add(diffActual.multiply(diffActual));
BigDecimal diffResidual = y.subtract(yPredicted);
ssRes = ssRes.add(diffResidual.multiply(diffResidual));
}
if (ssTot.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ONE; // 总平方和为0拟合完美
}
return BigDecimal.ONE.subtract(ssRes.divide(ssTot, MathContext.DECIMAL128), MathContext.DECIMAL128);
}
}