项目需要根据前端输入的样本数据,计算出对应的正态分布拟合曲线,后端使用的是spring boot,所以这里介绍一下Java的实现方式。
前端传入对应的上限、下限以及数据样本,后端计算出对应的均值、方差,再根据上下限均分若干份(这里300等份),利用正态分布函数计算出对应的拟合函数值,返回前端渲染。
{
"lsl": 0,
"sample": [
{
"featureValue": 0.1,
"label": "2023-03-24"
},
{
"featureValue": 0.049,
"label": "2023-03-23"
},
{
"featureValue": 0.09,
"label": "2023-03-22"
},
{
"featureValue": 0.08,
"label": "2023-03-21"
},
{
"featureValue": 0.04,
"label": "2023-03-20"
},
{
"featureValue": 0.04,
"label": "2023-03-19"
}
],
"usl": 0.1
}
其中,usl为控制上限,lsl为控制下限
后端接受到数据以后,计算出对应的均值、方差
不做过多解释
/**
* 平均值
*
* @param list 样本中的featureValue list
* @return
*/
private Double getMean(List<Double> list) {
return list.stream().mapToDouble(Double::valueOf).average().orElse(0);
}
根据方差公式做计算,公式如下:
σ ^ = ∑ i = 0 n ( x i − x ‾ ) 2 n \hat{\sigma} = \sqrt{\sum_{i = 0} ^n(x_i-\overline{x})^2 \over n} σ^=n∑i=0n(xi−x)2
根据以上公式计算即可
/**
* Sigma
*
* @param list 样本中的featureValue list
* @param mean 均值
* @return
*/
private Double getSigma(List<Double> list, Double mean) {
return Math.pow(list.stream().mapToDouble(item -> Math.pow(item - mean, 2)).sum() / list.size(), 0.5);
}
根据正太分布函数计算,函数如下:
f ( x ) = 1 σ 2 π e − ( x − μ ) 2 2 σ 2 f(x) = {1 \over \sigma\sqrt{2 \pi}}e^{ -{(x-\mu)^2 \over 2\sigma ^ 2}} f(x)=σ2π1e−2σ2(x−μ)2
根据这个函数计算即可,即,normalDistribution(double x, double mean, double sigma)
方法
/**
* 正太分布拟合曲线
*
* @param usl 控制上限
* @param lsl 控制下限
* @param mean 均值
* @param sigma 方差
* @return
*/
private List<List<Double>> featureNormal normalDistribution(Double usl, Double lsl, Double mean, Double sigma) {
List<List<Double>> featureNormal = new LinkedList<>();
double normalBegin = Math.min(mean - normalRange / 2, lsl);
double normalEnd = Math.max(mean + normalRange / 2, usl);
double normalRange = normalEnd - normalBegin;
normalBegin = normalBegin - Math.abs(normalBegin * 0.2);
normalEnd = normalEnd + Math.abs(normalEnd * 0.2);
//均分300个区间
Integer normalSize = 300;
double normalWidth = (normalEnd - normalBegin) / normalSize;
for (int i = 0; i < normalSize; i++) {
double x = normalBegin + i * normalWidth;
featureNormal.add(new LinkedList<>(Lists.newArrayList(x, normalDistribution(x, mean, sigma))));
}
}
/**
* 正太分布拟合值
*
* @param x x轴的值
* @param mean 均值
* @param sigma 方差
* @return
*/
private Double normalDistribution(double x, double mean, double sigma) {
return (1 / (sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-Math.pow((x - mean), 2) / (2 * Math.pow(sigma, 2)));
}
由此,便得到了指定区间、指定样本的正态分布拟合曲线,返给前端渲染即可。