using Accord.Math.Optimization; using MathNet.Numerics; using MathNet.Numerics.Interpolation; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearRegression; using MathNet.Numerics.Statistics; namespace IStation.Test { /// /// 泵曲线数据融合校正器(用于合并样条曲线和实测数据并进行优化) /// public class PumpCurveDataFusionCorrectorHelper { # region 配置参数 // 异常值检测阈值(基于标准正态分布,2.5对应98.76%置信区间) public const double ZScoreThreshold = 2.5; // 过渡区域宽度(单位与输入数据相同,控制曲线衔接平滑度) public const double TransitionWidth = 500; // 多项式拟合阶数(三次平衡拟合能力与过拟合风险) public const int PolynomialDegree = 3; // 优化器最大迭代次数(Cobyla算法收敛阈值) private const int OptimizationMaxIterations = 1000; # endregion /// /// 主校正方法(入口函数) /// /// 样条曲线X坐标 /// 样条曲线Y坐标 /// 原始实测X坐标 /// 原始实测Y坐标 /// /// mergedX: 融合后的X坐标 /// mergedY: 初步融合的Y坐标 /// optimizedX: 优化后的X坐标采样 /// optimizedY: 优化后的Y坐标结果 /// public (double[] mergedX, double[] mergedY, double[] optimizedX, double[] optimizedY) Corrent( double[] splineX, double[] splineY, double[] measuredXAll, double[] measuredYAll) { # region 步骤1:稳健回归与异常值过滤(最小二乘法) // 执行简单线性回归获取基准线 (double intercept, double slope) = SimpleRegression.Fit(measuredXAll, measuredYAll); // 计算预测值和残差 double[] predictedY = measuredXAll.Select(x => slope * x + intercept).ToArray(); double[] residuals = measuredYAll.Zip(predictedY, (m, p) => m - p).ToArray(); // 基于Z-Score的异常值过滤 var zScoreCalculator = new ZScore(residuals); bool[] validMask = zScoreCalculator.Scores .Select(score => Math.Abs(score) < ZScoreThreshold).ToArray(); // 过滤得到有效实测数据 var validData = measuredXAll.Zip(measuredYAll, validMask) .Where(t => t.Third) .Select(t => (t.First, t.Second)).ToList(); double[] measuredXValid = validData.Select(t => t.First).ToArray(); double[] measuredYValid = validData.Select(t => t.Second).ToArray(); # endregion if (measuredXValid.Length < 5) { Console.WriteLine("异常值过滤后数据过小"); return default; } return ProcessData(splineX, splineY, measuredXValid, measuredYValid, PolynomialDegree, TransitionWidth); } /// /// 核心数据处理流程(包含数据融合、过渡处理和优化) /// private (double[] mergedX, double[] mergedY, double[] optimizedX, double[] optimizedY) ProcessData( double[] splineX, double[] splineY, double[] measuredXValid, double[] measuredYValid, int polyDegree, double transitionWidth) { # region 步骤2:多项式拟合与初步融合 // 对有效实测数据进行多项式拟合 var polyFunc = Polynomial.Fit(measuredXValid, measuredYValid, polyDegree); double[] polyCoeffs = polyFunc.Coefficients.Reverse().ToArray(); // 注意系数顺序转换 // 生成融合后的X坐标序列(合并样条和实测范围) double[] mergedX = GenerateMergedX(splineX, measuredXValid, 200); // 创建样条插值器并生成初始融合曲线 var splineInterpolator = LinearSpline.InterpolateSorted(splineX, splineY); //double[] mergedY = mergedX.Select(x => // x < splineX[0] ? splineY[0] : // 左外推 // x > splineX.Last() ? splineY.Last() : // 右外推 // splineInterpolator.Interpolate(x) // 插值区域 //).ToArray(); double[] mergedY = mergedX.Select(x => //x < splineX[0] ? splineY[0] : // 左外推 //x > splineX.Last() ? splineY.Last() : // 右外推 splineInterpolator.Interpolate(x) // 插值区域 ).ToArray(); # endregion # region 步骤3:核心区域修正 // 在实测数据范围内应用多项式拟合结果 double minX = measuredXValid.Min(), maxX = measuredXValid.Max(); bool[] coreMask = mergedX.Select(x => x >= minX && x <= maxX).ToArray(); for (int i = 0; i < mergedX.Length; i++) { if (coreMask[i]) mergedY[i] = EvaluatePolynomial(polyCoeffs, mergedX[i]); } # endregion # region 步骤4:过渡区域处理 // 对左右过渡区域进行平滑处理 var splinePoly = Polynomial.Fit(splineX, splineY, polyDegree); double[] splineCoeffs = splinePoly.Coefficients.Reverse().ToArray(); // 左过渡区处理(样条->多项式) ApplyTransition( (minX - transitionWidth, minX), splineCoeffs, polyCoeffs, mergedX, ref mergedY ); // 右过渡区处理(多项式->样条) ApplyTransition( (maxX, maxX + transitionWidth), polyCoeffs, splineCoeffs, mergedX, ref mergedY ); # endregion # region 步骤5:带约束优化 // 数据标准化处理 double xMean = mergedX.Average(); double xStd = ArrayStatistics.PopulationStandardDeviation(mergedX); double[] xNorm = mergedX.Select(x => (x - xMean) / xStd).ToArray(); // 构建三次多项式优化问题 var X = Matrix.Build.DenseOfRowArrays( xNorm.Select(x => new[] { Math.Pow(x, 3), x * x, x, 1.0 }).ToArray() ); var initialParams = MultipleRegression.QR(X, Vector.Build.DenseOfArray(mergedY)); // 设置单调性约束(导数非负) var constraints = GenerateLinspace(xNorm.Min(), xNorm.Max(), 50) .Select(x => new NonlinearConstraint( numberOfVariables: 4, function: p => -Derivative(p, x), // 导数>=0 shouldBe: ConstraintType.GreaterThanOrEqualTo, value: 0 )).ToList(); // 执行COBYLA优化 var optimizer = new Cobyla( function: BuildObjectiveFunction(xNorm, mergedY), constraints: constraints.ToArray() ) { MaxIterations = OptimizationMaxIterations }; bool success = optimizer.Minimize(initialParams.ToArray()); double[] optimizedParams = optimizer.Solution; # endregion // 生成最终优化曲线 double[] optimizedX = GenerateLinspace( Math.Min(splineX.Min(), minX), Math.Max(splineX.Max(), maxX), 300 ); double[] optimizedY = optimizedX .Select(x => CubicModel(optimizedParams, (x - xMean) / xStd)) .ToArray(); return (mergedX, mergedY, optimizedX, optimizedY); } # region 工具方法 /// /// 三次多项式模型(降幂排列) /// private static double CubicModel(double[] param, double x) => param[0] * x * x * x + param[1] * x * x + param[2] * x + param[3]; /// /// 多项式导数(用于单调性约束) /// private static double Derivative(double[] param, double x) => 3 * param[0] * x * x + 2 * param[1] * x + param[2]; /// /// 构建目标函数(均方误差) /// private NonlinearObjectiveFunction BuildObjectiveFunction(double[] xNorm, double[] y) => new NonlinearObjectiveFunction(4, p => xNorm.Select((x, i) => Math.Pow(CubicModel(p, x) - y[i], 2)).Average() ); /// /// 多项式求值(霍纳法则) /// private static double EvaluatePolynomial(double[] coeffs, double x) => coeffs.Aggregate((sum, c) => sum * x + c); /// /// 生成等间距采样点 /// private static double[] GenerateLinspace(double start, double end, int numPoints) => Enumerable.Range(0, numPoints) .Select(i => start + i * (end - start) / (numPoints - 1)) .ToArray(); # endregion # region 过渡处理 /// /// 应用过渡混合(在两个多项式之间渐变) /// private static void ApplyTransition( (double start, double end) range, double[] baseCoeffs, double[] targetCoeffs, double[] xValues, ref double[] yValues) { for (int i = 0; i < xValues.Length; i++) { double x = xValues[i]; if (x < range.start || x > range.end) continue; double weight = (x - range.start) / (range.end - range.start); double baseVal = EvaluatePolynomial(baseCoeffs, x); double targetVal = EvaluatePolynomial(targetCoeffs, x); yValues[i] = (1 - weight) * baseVal + weight * targetVal; } } /// /// 生成合并的 x 值 /// public static double[] GenerateMergedX(double[] splineX, double[] measuredXValid, int numPoints) { // 生成等间距的点 double[] linspace = GenerateLinspace(measuredXValid.Min(), measuredXValid.Max(), numPoints); // 拼接并去重排序 var concat = splineX.Concat(linspace).ToArray(); var merged = concat.Distinct().OrderBy(x => x).ToArray(); return merged; } # endregion # region 辅助类 /// /// Z分数计算器(用于异常值检测) /// private class ZScore { public readonly double[] Scores; public ZScore(double[] data) { double mean = data.Average(); double std = data.StandardDeviation(); Scores = data.Select(x => (x - mean) / std).ToArray(); } } # endregion } }