添加工具类

This commit is contained in:
duwenyuan 2026-07-21 13:38:19 +08:00
parent 3eac85bf93
commit a7710c1bcb

View File

@ -1,11 +1,12 @@
package org.jeecg.util;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.entity.NuclideActConcIntvl;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
public class DistributionAnalysisToolkit {
/**
* 区间统计结果
@ -16,25 +17,26 @@ public class DistributionAnalysisToolkit {
private final int count;
private final List<Double> values;
private final Map<Integer, Integer> levelDistribution;
public IntervalStat(String interval, List<NuclideActConcIntvl> nuclideData) {
values=new ArrayList<>();
levelDistribution=new TreeMap<>();
public IntervalStat(String interval, List<NuclideActConcIntvl> nuclideData) {
values = new ArrayList<>();
levelDistribution = new TreeMap<>();
this.interval = interval;
this.count = nuclideData.size();
for (NuclideActConcIntvl nuclide : nuclideData) {
//获取浓度值
Double conc = nuclide.getConc();
if (conc != null) { // 过滤 conc null
values.add(conc);
}
Integer category = nuclide.getCategory();
if (category != null) { // 跳过 null
levelDistribution.merge(category, 1, Integer::sum);
} else {
// 数据库中存在记录 null 级别用特殊 key -1
// levelDistribution.merge(-1, 1, Integer::sum);
}
}
for (NuclideActConcIntvl nuclide : nuclideData) {
//获取浓度值
Double conc = nuclide.getConc();
if (conc != null) { // 过滤 conc null
values.add(conc);
}
Integer category = nuclide.getCategory();
if (category != null) { // 跳过 null
levelDistribution.merge(category, 1, Integer::sum);
} else {
// 数据库中存在记录 null 级别用特殊 key -1
// levelDistribution.merge(-1, 1, Integer::sum);
}
}
}
}
@ -50,8 +52,13 @@ public class DistributionAnalysisToolkit {
this.density = density;
}
public double getX() { return x; }
public double getDensity() { return density; }
public double getX() {
return x;
}
public double getDensity() {
return density;
}
}
/**
@ -66,15 +73,19 @@ public class DistributionAnalysisToolkit {
this.cumulativeProb = cumulativeProb;
}
public double getValue() { return value; }
public double getCumulativeProb() { return cumulativeProb; }
public double getValue() {
return value;
}
public double getCumulativeProb() {
return cumulativeProb;
}
}
/**
* 数据区间统计
*
* @param nuclideData 原始数据
* @param start 起始值
* @param step 区间宽度
* @return 区间统计结果列表
*/
//region
@ -111,50 +122,80 @@ public class DistributionAnalysisToolkit {
// return stats;
// }
//
//endregion
public static List<IntervalStat> calculateIntervalStats(List<NuclideActConcIntvl> nuclideData, double start, double step) {
//endregion
public static List<IntervalStat> calculateIntervalStats(List<NuclideActConcIntvl> nuclideData) {
if (nuclideData == null || nuclideData.isEmpty()) {
throw new IllegalArgumentException("数据不能为空");
}
// 计算结束边界
double maxConc = nuclideData.stream()
.mapToDouble(NuclideActConcIntvl::getConc)
.max()
.orElse(0.0);
double end = Math.ceil(maxConc / step) * step + step;
int numBins = calculateOptimalBins(nuclideData);
//int numBins = 50;
log.info("分箱个数",numBins);
//统计 min/max/count
DoubleSummaryStatistics statsData = nuclideData.stream()
.map(nuclideActConcIntvl -> nuclideActConcIntvl.getConc())
.filter(Objects::nonNull)
.filter(d -> !d.isNaN() && !d.isInfinite())
.mapToDouble(Double::doubleValue)
.summaryStatistics();
if (statsData.getCount() == 0) {
return Collections.emptyList();
}
double min = statsData.getMin();
double max = statsData.getMax();
log.info("数据min:",min);
log.info("数据max:",max);
//2. 处理所有值相同或极小范围关键避免 binWidth = 0
if (max - min < 1e-8) {
// 取值向下取整为起点向上扩展一个单位
min = Math.floor(min);
max = min + Math.max(1.0, Math.abs(min) * 0.1); // 避免 min=-1000 时扩展太小
}
double binWidth = (max - min) / numBins;
if (binWidth <= 0) {
binWidth = 1.0; // 极端防御
}
List<String> bins = new ArrayList<>();
// 初始化区间映射
Map<String, List<NuclideActConcIntvl>> intervalMap = new TreeMap<>();
for (double lower = start; lower < end; lower += step) {
double upper = lower + step;
String key = String.format("[%.1f, %.1f)", lower, upper);
for (int i = 0; i < numBins; i++) {
double start1 = min + i * binWidth;
double end1 = min + (i + 1) * binWidth;
// 最后一个区间使用闭区间语义更清晰
String key = i == numBins - 1
? String.format("[%.4f ~ %.4f]", start1, max)
: String.format("[%.4f ~ %.4f)", start1, end1);
bins.add(key);
intervalMap.put(key, new ArrayList<>());
}
// 分配数据到区间
for (NuclideActConcIntvl nuclide : nuclideData) {
double value=nuclide.getConc();
double lower = Math.floor(value / step) * step;
String key = String.format("[%.1f, %.1f)", lower, lower + step);
intervalMap.get(key).add(nuclide);
double value = nuclide.getConc();
// 计算当前对象属于哪个区间索引
int index = (int) ((value - min) / binWidth);
if (index < 0) {
index = 0;
}
if (index >= numBins) {
index = numBins - 1;
}
intervalMap.get(bins.get(index)).add(nuclide);
}
// 转换为统计结果对象
List<IntervalStat> stats = new ArrayList<>();
for (Map.Entry<String,List<NuclideActConcIntvl>> entry : intervalMap.entrySet()) {
stats.add(new IntervalStat(entry.getKey(), entry.getValue()));
for (Map.Entry<String, List<NuclideActConcIntvl>> entry : intervalMap.entrySet()) {
stats.add(new IntervalStat(entry.getKey(), entry.getValue()));
}
return stats;
}
/**
* 计算95%累积线
*
@ -218,7 +259,6 @@ public class DistributionAnalysisToolkit {
}
/**
* 核函数接口
*/
@ -228,10 +268,12 @@ public class DistributionAnalysisToolkit {
}
// 常用核函数实现
public static final KernelFunction GAUSSIAN_KERNEL = u -> Math.exp(-0.5 * u * u) / Math.sqrt(2 * Math.PI);
public static final KernelFunction EPANECHNIKOV_KERNEL = u -> (Math.abs(u) <= 1) ? 0.75 * (1 - u * u) : 0;
public static final KernelFunction TRIANGULAR_KERNEL = u -> (Math.abs(u) <= 1) ? 1 - Math.abs(u) : 0;
public static final KernelFunction GAUSSIAN_KERNEL =
u -> Math.exp(-0.5 * u * u) / Math.sqrt(2 * Math.PI);
public static final KernelFunction EPANECHNIKOV_KERNEL =
u -> (Math.abs(u) <= 1) ? 0.75 * (1 - u * u) : 0;
public static final KernelFunction TRIANGULAR_KERNEL =
u -> (Math.abs(u) <= 1) ? 1 - Math.abs(u) : 0;
/**
@ -239,7 +281,9 @@ public class DistributionAnalysisToolkit {
*/
public static double calculateBandwidthSilverman(List<Double> data) {
int n = data.size();
if (n <= 1) return 1.0;
if (n <= 1) {
return 1.0;
}
// 计算标准差
double mean = data.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
@ -310,15 +354,15 @@ public class DistributionAnalysisToolkit {
}
//获取浓度值集合
public static List<Double> convertConcToDoubleList(List<NuclideActConcIntvl> nuclideList) {
return nuclideList.stream() // 创建流
.map(NuclideActConcIntvl::getConc) // 提取conc值
.collect(Collectors.toList()); // 收集为List<Double>
}
//累积和
public static List<Double> cumulativeSum(List<Double> data) {
public static List<Double> cumulativeSum(List<Double> data) {
List<Double> result = new ArrayList<>();
double sum = 0.0;
@ -331,15 +375,32 @@ public class DistributionAnalysisToolkit {
}
public static int calculateOptimalBins(List<NuclideActConcIntvl> data) {
if (Objects.isNull(data)) {
return 5;
}
List<Double> values = data.stream()
.map(NuclideActConcIntvl::getConc)
.filter(Objects::nonNull)
.filter(d -> !d.isNaN() && !d.isInfinite())
.sorted()
.collect(Collectors.toList());
int n = values.size();
if (n < 2) {
return 5;
}
double q1 = values.get(n / 4);
double q3 = values.get((3 * n) / 4);
double iqr = q3 - q1;
if (iqr <= 0) {
return (int) Math.max(3, Math.ceil(Math.sqrt(n)));
}
double binWidth = 2 * iqr * Math.pow(n, -1.0 / 3.0);
double dataRange = values.get(n - 1) - values.get(0);
int bins = (int) Math.ceil(dataRange / binWidth);
bins = Math.max(3, Math.min(40, bins));
return bins;
}
}