添加工具类
This commit is contained in:
parent
3eac85bf93
commit
a7710c1bcb
|
|
@ -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,25 +17,26 @@ 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) {
|
|
||||||
values=new ArrayList<>();
|
public IntervalStat(String interval, List<NuclideActConcIntvl> nuclideData) {
|
||||||
levelDistribution=new TreeMap<>();
|
values = new ArrayList<>();
|
||||||
|
levelDistribution = new TreeMap<>();
|
||||||
this.interval = interval;
|
this.interval = interval;
|
||||||
this.count = nuclideData.size();
|
this.count = nuclideData.size();
|
||||||
for (NuclideActConcIntvl nuclide : nuclideData) {
|
for (NuclideActConcIntvl nuclide : nuclideData) {
|
||||||
//获取浓度值
|
//获取浓度值
|
||||||
Double conc = nuclide.getConc();
|
Double conc = nuclide.getConc();
|
||||||
if (conc != null) { // 过滤 conc null
|
if (conc != null) { // 过滤 conc null
|
||||||
values.add(conc);
|
values.add(conc);
|
||||||
}
|
}
|
||||||
Integer category = nuclide.getCategory();
|
Integer category = nuclide.getCategory();
|
||||||
if (category != null) { // 跳过 null
|
if (category != null) { // 跳过 null
|
||||||
levelDistribution.merge(category, 1, Integer::sum);
|
levelDistribution.merge(category, 1, Integer::sum);
|
||||||
} else {
|
} else {
|
||||||
// 数据库中存在记录 null 级别,用特殊 key 如 -1
|
// 数据库中存在记录 null 级别,用特殊 key 如 -1
|
||||||
// levelDistribution.merge(-1, 1, Integer::sum);
|
// levelDistribution.merge(-1, 1, Integer::sum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -111,50 +122,80 @@ public class DistributionAnalysisToolkit {
|
||||||
// return stats;
|
// return stats;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//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()) {
|
||||||
stats.add(new IntervalStat(entry.getKey(), entry.getValue()));
|
stats.add(new IntervalStat(entry.getKey(), entry.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算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,15 +354,15 @@ 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<>();
|
||||||
double sum = 0.0;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user