1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)


4  *


5  * This file is part of HeuristicLab.


6  *


7  * HeuristicLab is free software: you can redistribute it and/or modify


8  * it under the terms of the GNU General Public License as published by


9  * the Free Software Foundation, either version 3 of the License, or


10  * (at your option) any later version.


11  *


12  * HeuristicLab is distributed in the hope that it will be useful,


13  * but WITHOUT ANY WARRANTY; without even the implied warranty of


14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the


15  * GNU General Public License for more details.


16  *


17  * You should have received a copy of the GNU General Public License


18  * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.


19  */


20  #endregion


21 


22  using System;


23  using System.Collections.Generic;


24  using HeuristicLab.Common;


25 


26  namespace HeuristicLab.Problems.DataAnalysis {


27  public class OnlinePearsonsRCalculator : DeepCloneable, IOnlineCalculator {


28  private double sumX;


29  private double sumY;


30  private double sumWe;


31 


32  private double sumXX;


33  private double sumYY;


34  private double sumXY;


35 


36  private OnlineCalculatorError errorState;


37 


38  public double R {


39  get {


40  if (!(sumXX > 0.0 && sumYY > 0.0)) {


41  return (sumXX == sumYY) ? 1.0 : 0.0;


42  }


43  return sumXY / Math.Sqrt(sumXX * sumYY);


44  }


45  }


46 


47  public double MeanX { get { return sumX / sumWe; } }


48 


49  public double MeanY { get { return sumY / sumWe; } }


50 


51  public double NaiveCovariance { get { return sumXY / sumWe; } }


52 


53  public double SampleCovariance {


54  get {


55  if (sumWe > 1.0) {


56  errorState = OnlineCalculatorError.None;


57  return sumXY / (sumWe  1);


58  }


59  errorState = OnlineCalculatorError.InsufficientElementsAdded;


60  return double.NaN;


61  }


62  }


63 


64  public double NaiveVarianceX { get { return sumXX / sumWe; } }


65 


66  public double SampleVarianceX {


67  get {


68  if (sumWe > 1.0) {


69  errorState = OnlineCalculatorError.None;


70  return sumXX / (sumWe  1);


71  }


72  errorState = OnlineCalculatorError.InsufficientElementsAdded;


73  return double.NaN;


74  }


75  }


76 


77  public double NaiveStdevX { get { return Math.Sqrt(NaiveVarianceY); } }


78 


79  public double SampleStdevX { get { return Math.Sqrt(SampleVarianceX); } }


80 


81  public double NaiveVarianceY { get { return sumYY / sumWe; } }


82 


83  public double SampleVarianceY {


84  get {


85  if (sumWe > 1.0) {


86  errorState = OnlineCalculatorError.None;


87  return sumYY / (sumWe  1);


88  }


89  errorState = OnlineCalculatorError.InsufficientElementsAdded;


90  return double.NaN;


91  }


92  }


93 


94  public double NaiveStdevY { get { return Math.Sqrt(NaiveVarianceY); } }


95 


96  public double SampleStdevY { get { return Math.Sqrt(SampleVarianceX); } }


97 


98  public OnlinePearsonsRCalculator() { }


99 


100  protected OnlinePearsonsRCalculator(OnlinePearsonsRCalculator original, Cloner cloner)


101  : base(original, cloner) {


102  sumX = original.sumX;


103  sumY = original.sumY;


104  sumXX = original.sumXX;


105  sumYY = original.sumYY;


106  sumXY = original.sumXY;


107  sumWe = original.sumWe;


108  errorState = original.ErrorState;


109  }


110  public override IDeepCloneable Clone(Cloner cloner) {


111  return new OnlinePearsonsRCalculator(this, cloner);


112  }


113 


114  #region IOnlineCalculator Members


115  public OnlineCalculatorError ErrorState {


116  get { return errorState; }


117  }


118  public double Value {


119  get { return R; }


120  }


121  public void Reset() {


122  sumXX = sumYY = sumXY = sumX = sumY = sumWe = 0.0;


123  errorState = OnlineCalculatorError.None;


124  }


125 


126  public void Add(double x, double y) {


127  if (sumWe <= 0.0) {


128  sumX = x;


129  sumY = y;


130  sumWe = 1;


131  return;


132  }


133  // Delta to previous mean


134  double deltaX = x * sumWe  sumX, deltaY = y * sumWe  sumY;


135  double oldWe = sumWe;


136  // Incremental update


137  sumWe += 1;


138  double f = 1.0 / (sumWe * oldWe);


139  // Update


140  sumXX += f * deltaX * deltaX;


141  sumYY += f * deltaY * deltaY;


142  // should equal weight * deltaY * neltaX!


143  sumXY += f * deltaX * deltaY;


144  // Update means


145  sumX += x;


146  sumY += y;


147  }


148 


149  #endregion


150 


151  public static double Calculate(IEnumerable<double> first, IEnumerable<double> second, out OnlineCalculatorError errorState) {


152  var x = first.GetEnumerator(); x.MoveNext();


153  var y = second.GetEnumerator(); y.MoveNext();


154  double sumXX = 0.0, sumYY = 0.0, sumXY = 0.0;


155  double sumX = x.Current, sumY = y.Current;


156  int i = 1;


157 


158  // Inlined computation of Pearson correlation, to avoid allocating objects


159  // This is a numerically stabilized version, avoiding sumofsquares.


160  while (x.MoveNext() & y.MoveNext()) {


161  double xv = x.Current, yv = y.Current;


162  // Delta to previous mean


163  double deltaX = xv * i  sumX, deltaY = yv * i  sumY;


164  // Increment count first


165  double oldi = i; // Convert to double!


166  ++i;


167  double f = 1.0 / (i * oldi);


168  // Update


169  sumXX += f * deltaX * deltaX;


170  sumYY += f * deltaY * deltaY;


171  // should equal deltaY * deltaX!


172  sumXY += f * deltaX * deltaY;


173  // Update sums


174  sumX += xv;


175  sumY += yv;


176  }


177 


178  errorState = OnlineCalculatorError.None;


179  // One or both series were constant:


180  return !(sumXX > 0.0 && sumYY > 0.0) ? sumXX == sumYY ? 1.0 : 0.0 : //


181  sumXY / Math.Sqrt(sumXX * sumYY);


182  }


183  }


184  }

