From 7374ecb1b5d528bde9105db00c605ac4963a4e49 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 14 Oct 2018 18:55:39 +0200 Subject: [PATCH] Fit.Curve: extended to 2 and 3 parameters #597 --- src/Numerics.Tests/FitTests.cs | 20 ++++++++++++++++ src/Numerics/Fit.cs | 42 ++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/Numerics.Tests/FitTests.cs b/src/Numerics.Tests/FitTests.cs index c17c225f..9b7aa949 100644 --- a/src/Numerics.Tests/FitTests.cs +++ b/src/Numerics.Tests/FitTests.cs @@ -339,5 +339,25 @@ namespace MathNet.Numerics.UnitTests Assert.AreEqual(-0.467791 * z, resf(z), 1e-4); } } + + [Test] + public void FitsCurveToBestLine() + { + // Mathematica: Fit[{{1,4.986},{2,2.347},{3,2.061},{4,-2.995},{5,-2.352},{6,-5.782}}, {1, x}, x] + // -> 7.01013 - 2.08551*x + + var x = Enumerable.Range(1, 6).Select(Convert.ToDouble).ToArray(); + var y = new[] { 4.986, 2.347, 2.061, -2.995, -2.352, -5.782 }; + + var resp = Fit.Curve(x, y, (m, s, t) => m + s*t, 1.0, -1.0, 1e-12); + Assert.AreEqual(7.01013, resp.Item1, 1e-4); + Assert.AreEqual(-2.08551, resp.Item2, 1e-4); + + var resf = Fit.CurveFunc(x, y, (m, s, t) => m + s * t, 1.0, -1.0, 1e-12); + foreach (var z in Enumerable.Range(-3, 10)) + { + Assert.AreEqual(7.01013 - 2.08551 * z, resf(z), 1e-4); + } + } } } diff --git a/src/Numerics/Fit.cs b/src/Numerics/Fit.cs index 8d65c986..2802bdcb 100644 --- a/src/Numerics/Fit.cs +++ b/src/Numerics/Fit.cs @@ -345,13 +345,51 @@ namespace MathNet.Numerics } /// - /// Least-Squares fitting the points (x,y) to a line y : x -> a+b*x, - /// returning a function y' for the best fitting line. + /// Non-linear least-squares fitting the points (x,y) to an arbitrary function y : x -> f(p0, p1, x), + /// returning its best fitting parameter p0 and p1. + /// + public static Tuple Curve(double[] x, double[] y, Func f, double initialGuess0, double initialGuess1, double tolerance = 1e-8, int maxIterations = 1000) + { + return FindMinimum.OfFunction((p0, p1) => Distance.Euclidean(Generate.Map(x, t => f(p0, p1, t)), y), initialGuess0, initialGuess1, tolerance, maxIterations); + } + + /// + /// Non-linear least-squares fitting the points (x,y) to an arbitrary function y : x -> f(p0, p1, p2, x), + /// returning its best fitting parameter p0, p1 and p2. + /// + public static Tuple Curve(double[] x, double[] y, Func f, double initialGuess0, double initialGuess1, double initialGuess2, double tolerance = 1e-8, int maxIterations = 1000) + { + return FindMinimum.OfFunction((p0, p1, p2) => Distance.Euclidean(Generate.Map(x, t => f(p0, p1, p2, t)), y), initialGuess0, initialGuess1, initialGuess2, tolerance, maxIterations); + } + + /// + /// Non-linear least-squares fitting the points (x,y) to an arbitrary function y : x -> f(p, x), + /// returning a function y' for the best fitting curve. /// public static Func CurveFunc(double[] x, double[] y, Func f, double initialGuess, double tolerance = 1e-8, int maxIterations = 1000) { var parameters = Curve(x, y, f, initialGuess, tolerance, maxIterations); return z => f(parameters, z); } + + /// + /// Non-linear least-squares fitting the points (x,y) to an arbitrary function y : x -> f(p0, p1, x), + /// returning a function y' for the best fitting curve. + /// + public static Func CurveFunc(double[] x, double[] y, Func f, double initialGuess0, double initialGuess1, double tolerance = 1e-8, int maxIterations = 1000) + { + var parameters = Curve(x, y, f, initialGuess0, initialGuess1, tolerance, maxIterations); + return z => f(parameters.Item1, parameters.Item2, z); + } + + /// + /// Non-linear least-squares fitting the points (x,y) to an arbitrary function y : x -> f(p0, p1, p2, x), + /// returning a function y' for the best fitting curve. + /// + public static Func CurveFunc(double[] x, double[] y, Func f, double initialGuess0, double initialGuess1, double initialGuess2, double tolerance = 1e-8, int maxIterations = 1000) + { + var parameters = Curve(x, y, f, initialGuess0, initialGuess1, initialGuess2, tolerance, maxIterations); + return z => f(parameters.Item1, parameters.Item2, parameters.Item3, z); + } } }