/*
* Concept profile generation tool suite
* Copyright (C) 2015 Biosemantics Group, Erasmus University Medical Center,
* Rotterdam, The Netherlands
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package org.erasmusmc.math;
import java.util.Comparator;
import org.erasmusmc.collections.SortedList;
import org.erasmusmc.math.vector.ObjectAndDoubleEntry;
public class ClassificationResultEvaluator {
SortedList<ObjectAndDoubleEntry<Boolean>> classificationOutcome;
public int positives;
public int seedID;
public double AUC;
public double MAP;
public void evaluate(){
calculateAUC();
calculateMAP();
}
public int getNumberOfMatches(){
return classificationOutcome.size();
}
public ClassificationResultEvaluator(SortedList<ObjectAndDoubleEntry<Boolean>> classificationOutcome){
this.classificationOutcome=classificationOutcome;
positives = -1;
AUC = -1;
MAP = -1;
}
public void findPositives(){
positives = 0;
for (ObjectAndDoubleEntry<Boolean> vectorEntry: classificationOutcome ){
if (vectorEntry.key)
positives++;
}
}
public int getNumberOfPositives(){
if (positives<0){
findPositives();
}
return positives;
}
public void calculateMAP(){
MAP=0;
if (positives>0){
double falsePositives = 0;
double truePositives = 0;
int i =0 ;
while (i<classificationOutcome.size() && truePositives<positives){
if (classificationOutcome.get(i).key){
truePositives++;
MAP = MAP + truePositives/(truePositives+falsePositives);
}
else
falsePositives++;
i++;
}
MAP=MAP/(double)positives;
}
}
private double trapArea(double x1, double x2, double y1,double y2){
//this function calculates the area of an trapezoid whose corners
//lie at given x and y values;
double base = x1-x2;
double avheight = (y1+y2)/2d;
return base*avheight;
}
public double calculateAUC(){
if (positives<0){
findPositives();
}
AUC=0;
if (positives>0){
int falsePositives = 0;
int previousFalsePositives = falsePositives;
//int falseNegatives = positives;
int truePositives = 0;
int previousTruePositives = truePositives;
//int trueNegatives=classificationOutcome.size()-positives;
double previousValue=Double.NEGATIVE_INFINITY;
for (ObjectAndDoubleEntry<Boolean> entry: classificationOutcome ){
if(entry.value!=previousValue){
AUC+=trapArea(falsePositives,previousFalsePositives,truePositives,previousTruePositives);
previousValue = entry.value;
previousFalsePositives = falsePositives;
previousTruePositives = truePositives;
}
if (entry.key){
truePositives++;
}
else {
falsePositives++;
}
}
AUC+=trapArea(falsePositives,previousFalsePositives,truePositives,previousTruePositives);
AUC/=(double)(positives*(getNumberOfMatches()-positives));
}
return AUC;
}
public static Comparator <ObjectAndDoubleEntry<Boolean>> classificationResultComparatorDescending() {
return new Comparator<ObjectAndDoubleEntry<Boolean>>() {
public int compare(ObjectAndDoubleEntry object1, ObjectAndDoubleEntry object2) {
if (object1.value < object2.value){
return 1;
}
else if (object1.value == object2.value){
return 0;
}
else {
return -1;
}
}
};
}
public static Comparator <ObjectAndDoubleEntry<Boolean>> classificationResultComparatorAscending() {
return new Comparator<ObjectAndDoubleEntry<Boolean>>() {
public int compare(ObjectAndDoubleEntry object1, ObjectAndDoubleEntry object2) {
if (object1.value > object2.value){
return 1;
}
else if (object1.value == object2.value){
return 0;
}
else {
return -1;
}
}
};
}
}