1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) 20022014 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 System.Linq;


25  using System.Text;


26  using HeuristicLab.Common;


27 


28  namespace HeuristicLab.Problems.DataAnalysis {


29  public class ClassificationPerformanceMeasuresCalculator {


30 


31  public ClassificationPerformanceMeasuresCalculator(double positiveClassValue) {


32  this.positiveClassValue = positiveClassValue;


33  Reset();


34  }


35 


36  #region Properties


37  private double positiveClassValue;


38  private int truePositiveCount, falsePositiveCount, trueNegativeCount, falseNegativeCount;


39 


40  public double TruePositiveRate {


41  get {


42  double divisor = truePositiveCount + falseNegativeCount;


43  return divisor.IsAlmost(0) ? double.NaN : truePositiveCount / divisor;


44  }


45  }


46  public double TrueNegativeRate {


47  get {


48  double divisor = falsePositiveCount + trueNegativeCount;


49  return divisor.IsAlmost(0) ? double.NaN : trueNegativeCount / divisor;


50  }


51  }


52  public double PositivePredictiveValue {


53  get {


54  double divisor = truePositiveCount + falsePositiveCount;


55  return divisor.IsAlmost(0) ? double.NaN : truePositiveCount / divisor;


56  }


57  }


58  public double NegativePredictiveValue {


59  get {


60  double divisor = trueNegativeCount + falseNegativeCount;


61  return divisor.IsAlmost(0) ? double.NaN : trueNegativeCount / divisor;


62  }


63  }


64  public double FalsePositiveRate {


65  get {


66  double divisor = falsePositiveCount + trueNegativeCount;


67  return divisor.IsAlmost(0) ? double.NaN : falsePositiveCount / divisor;


68  }


69  }


70  public double FalseDiscoveryRate {


71  get {


72  double divisor = falsePositiveCount + truePositiveCount;


73  return divisor.IsAlmost(0) ? double.NaN : falsePositiveCount / divisor;


74  }


75  }


76 


77  private OnlineCalculatorError errorState;


78  public OnlineCalculatorError ErrorState {


79  get { return errorState; }


80  }


81  #endregion


82 


83  public void Reset() {


84  truePositiveCount = 0;


85  falseNegativeCount = 0;


86  trueNegativeCount = 0;


87  falseNegativeCount = 0;


88  errorState = OnlineCalculatorError.InsufficientElementsAdded;


89  }


90 


91  public void Add(double originalClassValue, double estimatedClassValue) {


92  // ignore cases where original is NaN completely


93  if (!double.IsNaN(originalClassValue)) {


94  if (originalClassValue.IsAlmost(positiveClassValue)


95   estimatedClassValue.IsAlmost(positiveClassValue)) { //positive class/positive class estimation


96  if (estimatedClassValue.IsAlmost(originalClassValue)) {


97  truePositiveCount++;


98  } else {


99  if (estimatedClassValue.IsAlmost(positiveClassValue)) //misclassification of the negative class


100  falsePositiveCount++;


101  else //misclassification of the positive class


102  falseNegativeCount++;


103  }


104  } else { //negative class/negative class estimation


105  //In a multiclass classification all misclassifications of the negative class


106  //will be treated as true negatives except on positive class estimations


107  trueNegativeCount++;


108  }


109  errorState = OnlineCalculatorError.None; // number of (nonNaN) samples >= 1


110  }


111  }


112 


113  public void Calculate(IEnumerable<double> originalClassValues, IEnumerable<double> estimatedClassValues,


114  out OnlineCalculatorError errorState) {


115  IEnumerator<double> originalEnumerator = originalClassValues.GetEnumerator();


116  IEnumerator<double> estimatedEnumerator = estimatedClassValues.GetEnumerator();


117 


118  // always move forward both enumerators (do not use shortcircuit evaluation!)


119  while (originalEnumerator.MoveNext() & estimatedEnumerator.MoveNext()) {


120  double original = originalEnumerator.Current;


121  double estimated = estimatedEnumerator.Current;


122  Add(original, estimated);


123  if (ErrorState != OnlineCalculatorError.None) break;


124  }


125 


126  // check if both enumerators are at the end to make sure both enumerations have the same length


127  if (ErrorState == OnlineCalculatorError.None &&


128  (estimatedEnumerator.MoveNext()  originalEnumerator.MoveNext())) {


129  throw new ArgumentException("Number of elements in originalValues and estimatedValues enumerations doesn't match.");


130  } else {


131  errorState = ErrorState;


132  }


133  }


134  }


135  }

