添加工具类

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; package org.jeecg.util;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.entity.NuclideActConcIntvl; import org.jeecg.entity.NuclideActConcIntvl;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j
public class DistributionAnalysisToolkit { public class DistributionAnalysisToolkit {
/** /**
* 区间统计结果 * 区间统计结果
@ -16,6 +17,7 @@ public class DistributionAnalysisToolkit {
private final int count; private final int count;
private final List<Double> values; private final List<Double> values;
private final Map<Integer, Integer> levelDistribution; private final Map<Integer, Integer> levelDistribution;
public IntervalStat(String interval, List<NuclideActConcIntvl> nuclideData) { public IntervalStat(String interval, List<NuclideActConcIntvl> nuclideData) {
values = new ArrayList<>(); values = new ArrayList<>();
levelDistribution = new TreeMap<>(); levelDistribution = new TreeMap<>();
@ -50,8 +52,13 @@ public class DistributionAnalysisToolkit {
this.density = density; this.density = density;
} }
public double getX() { return x; } public double getX() {
public double getDensity() { return density; } return x;
}
public double getDensity() {
return density;
}
} }
/** /**
@ -66,15 +73,19 @@ public class DistributionAnalysisToolkit {
this.cumulativeProb = cumulativeProb; this.cumulativeProb = cumulativeProb;
} }
public double getValue() { return value; } public double getValue() {
public double getCumulativeProb() { return cumulativeProb; } return value;
} }
public double getCumulativeProb() {
return cumulativeProb;
}
}
/** /**
* 数据区间统计 * 数据区间统计
* *
* @param nuclideData 原始数据 * @param nuclideData 原始数据
* @param start 起始值
* @param step 区间宽度
* @return 区间统计结果列表 * @return 区间统计结果列表
*/ */
//region //region
@ -112,36 +123,69 @@ public class DistributionAnalysisToolkit {
// } // }
// //
//endregion //endregion
public static List<IntervalStat> calculateIntervalStats(List<NuclideActConcIntvl> nuclideData) {
public static List<IntervalStat> calculateIntervalStats(List<NuclideActConcIntvl> nuclideData, double start, double step) {
if (nuclideData == null || nuclideData.isEmpty()) { if (nuclideData == null || nuclideData.isEmpty()) {
throw new IllegalArgumentException("数据不能为空"); throw new IllegalArgumentException("数据不能为空");
} }
// 计算结束边界 int numBins = calculateOptimalBins(nuclideData);
double maxConc = nuclideData.stream() //int numBins = 50;
.mapToDouble(NuclideActConcIntvl::getConc) log.info("分箱个数",numBins);
.max() //统计 min/max/count
.orElse(0.0); DoubleSummaryStatistics statsData = nuclideData.stream()
double end = Math.ceil(maxConc / step) * step + step; .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<>(); Map<String, List<NuclideActConcIntvl>> intervalMap = new TreeMap<>();
for (double lower = start; lower < end; lower += step) { for (int i = 0; i < numBins; i++) {
double upper = lower + step; double start1 = min + i * binWidth;
String key = String.format("[%.1f, %.1f)", lower, upper); 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<>()); intervalMap.put(key, new ArrayList<>());
} }
// 分配数据到区间 // 分配数据到区间
for (NuclideActConcIntvl nuclide : nuclideData) { for (NuclideActConcIntvl nuclide : nuclideData) {
double value = nuclide.getConc(); double value = nuclide.getConc();
double lower = Math.floor(value / step) * step; // 计算当前对象属于哪个区间索引
String key = String.format("[%.1f, %.1f)", lower, lower + step); int index = (int) ((value - min) / binWidth);
if (index < 0) {
intervalMap.get(key).add(nuclide); index = 0;
}
if (index >= numBins) {
index = numBins - 1;
}
intervalMap.get(bins.get(index)).add(nuclide);
} }
// 转换为统计结果对象 // 转换为统计结果对象
List<IntervalStat> stats = new ArrayList<>(); List<IntervalStat> stats = new ArrayList<>();
for (Map.Entry<String, List<NuclideActConcIntvl>> entry : intervalMap.entrySet()) { for (Map.Entry<String, List<NuclideActConcIntvl>> entry : intervalMap.entrySet()) {
@ -152,9 +196,6 @@ public class DistributionAnalysisToolkit {
} }
/** /**
* 计算95%累积线 * 计算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 GAUSSIAN_KERNEL =
public static final KernelFunction EPANECHNIKOV_KERNEL = u -> (Math.abs(u) <= 1) ? 0.75 * (1 - u * u) : 0; u -> Math.exp(-0.5 * u * u) / Math.sqrt(2 * Math.PI);
public static final KernelFunction TRIANGULAR_KERNEL = u -> (Math.abs(u) <= 1) ? 1 - Math.abs(u) : 0; 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) { public static double calculateBandwidthSilverman(List<Double> data) {
int n = data.size(); 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); double mean = data.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
@ -310,13 +354,13 @@ public class DistributionAnalysisToolkit {
} }
//获取浓度值集合 //获取浓度值集合
public static List<Double> convertConcToDoubleList(List<NuclideActConcIntvl> nuclideList) { public static List<Double> convertConcToDoubleList(List<NuclideActConcIntvl> nuclideList) {
return nuclideList.stream() // 创建流 return nuclideList.stream() // 创建流
.map(NuclideActConcIntvl::getConc) // 提取conc值 .map(NuclideActConcIntvl::getConc) // 提取conc值
.collect(Collectors.toList()); // 收集为List<Double> .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<>(); List<Double> result = new ArrayList<>();
@ -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;
}
} }