/*********************************************************************** This file is part of KEEL-software, the Data Mining tool for regression, classification, clustering, pattern mining and so on. Copyright (C) 2004-2010 F. Herrera (herrera@decsai.ugr.es) L. S�nchez (luciano@uniovi.es) J. Alcal�-Fdez (jalcala@decsai.ugr.es) S. Garc�a (sglopez@ujaen.es) A. Fern�ndez (alberto.fernandez@ujaen.es) J. Luengo (julianlm@decsai.ugr.es) This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ **********************************************************************/ /** * * File: KNNAdaptive.java * * The KNN Adaptive Algorithm. * Similar to the KNN algorithm, distances are weighted by the distance * from the training instance to its nearest enemy. * * @author Written by Joaqu�n Derrac (University of Granada) 13/11/2008 * @version 1.0 * @since JDK1.5 * */ package keel.Algorithms.Lazy_Learning.KNNAdaptive; import keel.Algorithms.Lazy_Learning.LazyAlgorithm; import org.core.*; import java.util.StringTokenizer; public class KNNAdaptive extends LazyAlgorithm{ //Parameters private int k; private int distanceType; //Constants private static final int MANHATTAN = 1; private static final int EUCLIDEAN = 2; //Adictional structures double radius[]; /** * The main method of the class * * @param script Name of the configuration script * */ public KNNAdaptive (String script) { readDataFiles(script); //Naming the algorithm name="KNN Adaptative"; //Inicialization of auxiliar structures radius=new double[trainData.length]; //Initialization stuff ends here. So, we can start time-counting setInitialTime(); } //end-method /** * Reads configuration script, to extract the parameter's values. * * @param script Name of the configuration script * */ protected void readParameters (String script) { String file; String line; StringTokenizer fileLines, tokens; file = Files.readFile (script); fileLines = new StringTokenizer (file,"\n\r"); //Discard in/out files definition fileLines.nextToken(); fileLines.nextToken(); fileLines.nextToken(); //Getting the number of neighbors line = fileLines.nextToken(); tokens = new StringTokenizer (line, "="); tokens.nextToken(); k = Integer.parseInt(tokens.nextToken().substring(1)); //Getting the type of distance function line = fileLines.nextToken(); tokens = new StringTokenizer (line, "="); tokens.nextToken(); distanceType = tokens.nextToken().substring(1).equalsIgnoreCase("Euclidean")?EUCLIDEAN:MANHATTAN; }//end-method /** * Evaluates a instance to predict its class. * * @param example Instance evaluated * @return Class predicted * */ protected int evaluate (double example[]) { double minDist[]; int nearestN[]; int selectedClasses[]; double dist; int prediction; int predictionValue; boolean stop; nearestN = new int[k]; minDist = new double[k]; for (int i=0; i<k; i++) { nearestN[i] = -1; minDist[i] = Double.MAX_VALUE; } //KNN Method starts here for (int i=0; i<trainData.length; i++) { dist = adaptativeDistance(trainData[i],example,i); if (dist > 0.0){ //leave-one-out //see if it's nearer than our previous selected neigbours stop=false; for(int j=0;j<k && !stop;j++){ if (dist < minDist[j]) { for (int l = k - 1; l >= j+1; l--) { minDist[l] = minDist[l - 1]; nearestN[l] = nearestN[l - 1]; } minDist[j] = dist; nearestN[j] = i; stop=true; } } } } //we have check all the instances... see what is the most present class selectedClasses= new int[nClasses]; for (int i=0; i<nClasses; i++) { selectedClasses[i] = 0; } for (int i=0; i<k; i++) { selectedClasses[trainOutput[nearestN[i]]]+=1; } prediction=0; predictionValue=selectedClasses[0]; for (int i=1; i<nClasses; i++) { if (predictionValue < selectedClasses[i]) { predictionValue = selectedClasses[i]; prediction = i; } } return prediction; } //end-method /** * Calculates the distance between two instances * * @param instance1 First instance * @param instance2 Second instance * @return Distance calculated * */ private double distance(double instance1[],double instance2[]){ double dist=0.0; switch (distanceType){ case MANHATTAN: dist=manhattanDistance(instance1,instance2); break; case EUCLIDEAN: default: dist=euclideanDistance(instance1,instance2); break; }; return dist; } //end-method /** * Calculates the adaptative distance between two instances * * @param instance1 First instance * @param instance2 Second instance * @param index Index of train instance in radius structure * @return Distance calculated * */ private double adaptativeDistance(double instance1[],double instance2[],int index){ double dist; dist=distance(instance1,instance2); //Apply the radius conversion dist=dist/radius[index]; return dist; } //end-method /** * Precalculates the radius of each train instance * */ public void calculateRadius(){ int ownClass; double minDist; double dist; for(int i=0;i<trainData.length;i++){ ownClass=trainOutput[i]; minDist=Double.MAX_VALUE; //Search the nearest enemy (instance from another class) for(int j=0; j<trainData.length;j++){ if(ownClass!=trainOutput[j]){ dist = distance(trainData[i],trainData[j]); if(dist<minDist){ minDist=dist; } } } radius[i]=minDist; } } //end-method } //end-class