1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) 20022015 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 ClassificationPerformanceMeasuresCalculator {


28 


29  public ClassificationPerformanceMeasuresCalculator(string positiveClassName, double positiveClassValue) {


30  this.positiveClassName = positiveClassName;


31  this.positiveClassValue = positiveClassValue;


32  Reset();


33  }


34 


35  #region Properties


36  private int truePositiveCount, falsePositiveCount, trueNegativeCount, falseNegativeCount;


37 


38  private readonly string positiveClassName;


39  public string PositiveClassName {


40  get { return positiveClassName; }


41  }


42 


43  private readonly double positiveClassValue;


44  public double PositiveClassValue {


45  get { return positiveClassValue; }


46  }


47  public double TruePositiveRate {


48  get {


49  double divisor = truePositiveCount + falseNegativeCount;


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


51  }


52  }


53  public double TrueNegativeRate {


54  get {


55  double divisor = falsePositiveCount + trueNegativeCount;


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


57  }


58  }


59  public double PositivePredictiveValue {


60  get {


61  double divisor = truePositiveCount + falsePositiveCount;


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


63  }


64  }


65  public double NegativePredictiveValue {


66  get {


67  double divisor = trueNegativeCount + falseNegativeCount;


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


69  }


70  }


71  public double FalsePositiveRate {


72  get {


73  double divisor = falsePositiveCount + trueNegativeCount;


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


75  }


76  }


77  public double FalseDiscoveryRate {


78  get {


79  double divisor = falsePositiveCount + truePositiveCount;


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


81  }


82  }


83 


84  private OnlineCalculatorError errorState;


85  public OnlineCalculatorError ErrorState {


86  get { return errorState; }


87  }


88  #endregion


89 


90  public void Reset() {


91  truePositiveCount = 0;


92  falseNegativeCount = 0;


93  trueNegativeCount = 0;


94  falseNegativeCount = 0;


95  errorState = OnlineCalculatorError.InsufficientElementsAdded;


96  }


97 


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


99  // ignore cases where original is NaN completely


100  if (double.IsNaN(originalClassValue)) return;


101 


102  if (originalClassValue.IsAlmost(positiveClassValue)


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


104  if (estimatedClassValue.IsAlmost(originalClassValue)) {


105  truePositiveCount++;


106  } else {


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


108  falsePositiveCount++;


109  else //misclassification of the positive class


110  falseNegativeCount++;


111  }


112  } else { //negative class/negative class estimation


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


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


115  trueNegativeCount++;


116  }


117 


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


119  }


120 


121  public void Calculate(IEnumerable<double> originalClassValues, IEnumerable<double> estimatedClassValues) {


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


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


124 


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


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


127  double original = originalEnumerator.Current;


128  double estimated = estimatedEnumerator.Current;


129  Add(original, estimated);


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


131  }


132 


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


134  if (ErrorState == OnlineCalculatorError.None && (estimatedEnumerator.MoveNext()  originalEnumerator.MoveNext())) {


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


136  }


137  errorState = ErrorState;


138  }


139  }


140  }

