[Libreoffice-commits] core.git: chart2/source

Deena Francis (via logerrit) logerrit at kemper.freedesktop.org
Wed Dec 18 21:53:55 UTC 2019


 chart2/source/inc/PolynomialRegressionCurveCalculator.hxx   |   10 
 chart2/source/tools/PolynomialRegressionCurveCalculator.cxx |  137 ++++++++----
 2 files changed, 111 insertions(+), 36 deletions(-)

New commits:
commit 8df6f6ec12972ce2c14a162e6f4dd2c0d32367ef
Author:     Deena Francis <deena.francis at gmail.com>
AuthorDate: Mon Dec 16 18:47:11 2019 +0530
Commit:     Tomaž Vajngerl <quikee at gmail.com>
CommitDate: Wed Dec 18 22:52:17 2019 +0100

    tdf#128995: Special case for single variable regression...
    
    like in LINEST implementation in Calc. Use a straightforward
    regression solver in this case, so that it is easier to
    handle the numerical error in the intercept term using
    ::rtl::math::approxSub().
    
    Change-Id: I627c0c48e377cac5385a85050c4f472ee963f3d6
    Reviewed-on: https://gerrit.libreoffice.org/85222
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>

diff --git a/chart2/source/inc/PolynomialRegressionCurveCalculator.hxx b/chart2/source/inc/PolynomialRegressionCurveCalculator.hxx
index 6037fc742a78..e47d882d4903 100644
--- a/chart2/source/inc/PolynomialRegressionCurveCalculator.hxx
+++ b/chart2/source/inc/PolynomialRegressionCurveCalculator.hxx
@@ -22,6 +22,11 @@
 #include "RegressionCurveCalculator.hxx"
 #include <vector>
 
+namespace RegressionCalculationHelper
+{
+    typedef std::pair< std::vector< double >, std::vector< double > > tDoubleVectorPair;
+}
+
 namespace chart
 {
 
@@ -44,6 +49,11 @@ private:
         const css::uno::Sequence<double>& aXValues,
         const css::uno::Sequence<double>& aYValues ) override;
 
+    void computeCorrelationCoefficient(
+        RegressionCalculationHelper::tDoubleVectorPair& rValues,
+        const sal_Int32 aNoValues,
+        double yAverage );
+
     std::vector<double> mCoefficients;
 };
 
diff --git a/chart2/source/tools/PolynomialRegressionCurveCalculator.cxx b/chart2/source/tools/PolynomialRegressionCurveCalculator.cxx
index 8658f6004c1e..c1e17594a316 100644
--- a/chart2/source/tools/PolynomialRegressionCurveCalculator.cxx
+++ b/chart2/source/tools/PolynomialRegressionCurveCalculator.cxx
@@ -31,12 +31,60 @@ using namespace com::sun::star;
 namespace chart
 {
 
+static double lcl_GetDotProduct(std::vector<double>& aVec1, std::vector<double>& aVec2)
+{
+    double fResult = 0.0;
+    assert(aVec1.size() == aVec2.size());
+    for (size_t i = 0; i < aVec1.size(); ++i)
+        fResult += aVec1[i] * aVec2[i];
+    return fResult;
+}
+
 PolynomialRegressionCurveCalculator::PolynomialRegressionCurveCalculator()
 {}
 
 PolynomialRegressionCurveCalculator::~PolynomialRegressionCurveCalculator()
 {}
 
+void PolynomialRegressionCurveCalculator::computeCorrelationCoefficient(
+    RegressionCalculationHelper::tDoubleVectorPair& rValues,
+    const sal_Int32 aNoValues,
+    double yAverage )
+{
+    double aSumError = 0.0;
+    double aSumTotal = 0.0;
+    double aSumYpred2 = 0.0;
+
+    for( sal_Int32 i = 0; i < aNoValues; i++ )
+    {
+        double xValue = rValues.first[i];
+        double yActual = rValues.second[i];
+        double yPredicted = getCurveValue( xValue );
+        aSumTotal += (yActual - yAverage) * (yActual - yAverage);
+        aSumError += (yActual - yPredicted) * (yActual - yPredicted);
+        if(mForceIntercept)
+            aSumYpred2 += (yPredicted - mInterceptValue) * (yPredicted - mInterceptValue);
+    }
+
+    double aRSquared = 0.0;
+    if(mForceIntercept)
+    {
+        if (auto const div = aSumError + aSumYpred2)
+        {
+            aRSquared = aSumYpred2 / div;
+        }
+    }
+    else if (aSumTotal != 0.0)
+    {
+        aRSquared = 1.0 - (aSumError / aSumTotal);
+    }
+
+    if (aRSquared > 0.0)
+        m_fCorrelationCoeffitient = std::sqrt(aRSquared);
+    else
+        m_fCorrelationCoeffitient = 0.0;
+}
+
 // ____ XRegressionCurveCalculator ____
 void SAL_CALL PolynomialRegressionCurveCalculator::recalculateRegression(
     const uno::Sequence< double >& aXValues,
@@ -56,9 +104,6 @@ void SAL_CALL PolynomialRegressionCurveCalculator::recalculateRegression(
 
     double yAverage = 0.0;
 
-    std::vector<double> aQRTransposed;
-    aQRTransposed.resize(aNoValues * aNoPowers, 0.0);
-
     std::vector<double> yVector;
     yVector.resize(aNoValues, 0.0);
 
@@ -75,6 +120,57 @@ void SAL_CALL PolynomialRegressionCurveCalculator::recalculateRegression(
         yAverage /= aNoValues;
     }
 
+    // Special case for single variable regression like in LINEST
+    // implementation in Calc.
+    if (mDegree == 1)
+    {
+        std::vector<double> xVector;
+        xVector.resize(aNoValues, 0.0);
+        double xAverage = 0.0;
+
+        for(sal_Int32 i = 0; i < aNoValues; ++i)
+        {
+            double xValue = aValues.first[i];
+            xVector[i] = xValue;
+            xAverage += xValue;
+        }
+        if (aNoValues != 0)
+        {
+            xAverage /= aNoValues;
+        }
+
+        if (!mForceIntercept)
+        {
+            for (sal_Int32 i = 0; i < aNoValues; ++i)
+            {
+                xVector[i] -= xAverage;
+                yVector[i] -= yAverage;
+            }
+        }
+        double fSumXY = lcl_GetDotProduct(xVector, yVector);
+        double fSumX2 = lcl_GetDotProduct(xVector, xVector);
+
+        double fSlope = fSumXY / fSumX2;
+
+        if (!mForceIntercept)
+        {
+            mInterceptValue = ::rtl::math::approxSub(yAverage, fSlope * xAverage);
+            mCoefficients[0] = mInterceptValue;
+            mCoefficients[1] = fSlope;
+        }
+        else
+        {
+            mCoefficients[0] = fSlope;
+            mCoefficients.insert(mCoefficients.begin(), mInterceptValue);
+        }
+
+        computeCorrelationCoefficient(aValues, aNoValues, yAverage);
+        return;
+    }
+
+    std::vector<double> aQRTransposed;
+    aQRTransposed.resize(aNoValues * aNoPowers, 0.0);
+
     for(sal_Int32 j = 0; j < aNoPowers; j++)
     {
         sal_Int32 aPower = mForceIntercept ? j+1 : j;
@@ -167,39 +263,8 @@ void SAL_CALL PolynomialRegressionCurveCalculator::recalculateRegression(
         mCoefficients.insert(mCoefficients.begin(), mInterceptValue);
     }
 
-    // Calculate correlation coeffitient
-    double aSumError = 0.0;
-    double aSumTotal = 0.0;
-    double aSumYpred2 = 0.0;
-
-    for( sal_Int32 i = 0; i < aNoValues; i++ )
-    {
-        double xValue = aValues.first[i];
-        double yActual = aValues.second[i];
-        double yPredicted = getCurveValue( xValue );
-        aSumTotal += (yActual - yAverage) * (yActual - yAverage);
-        aSumError += (yActual - yPredicted) * (yActual - yPredicted);
-        if(mForceIntercept)
-            aSumYpred2 += (yPredicted - mInterceptValue) * (yPredicted - mInterceptValue);
-    }
-
-    double aRSquared = 0.0;
-    if(mForceIntercept)
-    {
-        if (auto const div = aSumError + aSumYpred2)
-        {
-            aRSquared = aSumYpred2 / div;
-        }
-    }
-    else if (aSumTotal != 0.0)
-    {
-        aRSquared = 1.0 - (aSumError / aSumTotal);
-    }
-
-    if (aRSquared > 0.0)
-        m_fCorrelationCoeffitient = std::sqrt(aRSquared);
-    else
-        m_fCorrelationCoeffitient = 0.0;
+    // Calculate correlation coefficient
+    computeCorrelationCoefficient(aValues, aNoValues, yAverage);
 }
 
 double SAL_CALL PolynomialRegressionCurveCalculator::getCurveValue( double x )


More information about the Libreoffice-commits mailing list