package weka.core;
import weka.core.neighboursearch.PerformanceStats;
public class GowerDistance extends EuclideanDistance {
private static final long serialVersionUID = 1L;
double[] ranges;
private GowerDistance(){
super();
}
public GowerDistance(Instances data) {
super(data);
ranges=findRanges(data);
}
double[] findRanges(Instances d){
int classIndex=d.classIndex();
double[] r;
if(classIndex>0)
r= new double[d.numAttributes()-1];
else
r= new double[d.numAttributes()];
int c=0;
for(int j=0;j<d.numAttributes();j++){
if(j!=classIndex){
//Find min and max range for attribute i
double min=d.instance(0).value(0);
double max=min;
for(int i=1;i<d.numInstances();i++){
double x=d.instance(i).value(j);
if(x>max)
max=x;
if(x<min)
min=x;
}
//Set range as max-min
r[c]=max-min;
c++;
}
}
return r;
}
//Needs overriding to avoid cutoff check
public double distance(Instance first, Instance second){
return distance(first, second, Double.POSITIVE_INFINITY, null, false);
}
public double distance(Instance first, Instance second, PerformanceStats stats) { //debug method pls remove after use
return distance(first, second, Double.POSITIVE_INFINITY, stats, false);
}
public double distance(Instance first, Instance second, double cutOffValue, PerformanceStats stats){
return distance(first,second,cutOffValue,stats,false);
}
public double distance(Instance first, Instance second, double cutOffValue, PerformanceStats stats, boolean print) {
return distance(first,second,cutOffValue);
}
public double distance(Instance first, Instance second, double cutOffValue) {
//Get the double arrays without the class value.
int classIndex=first.classIndex();
double[] f1,f2;
if(classIndex>=0){
f1=new double[first.numAttributes()-1];
f2=new double[second.numAttributes()-1];
int c=0;
for(int i=0;i<first.numAttributes();i++){
if(i!=classIndex){
f1[c]=first.value(i);
f2[c]=second.value(i);
c++;
}
}
}else{
f1=new double[first.numAttributes()];
f2=new double[second.numAttributes()];
for(int i=0;i<first.numAttributes();i++){
f1[i]=first.value(i);
f2[i]=second.value(i);
}
}
return distance(f1,f2,cutOffValue);
}
/** This is the method that actually does the calculation.
* Only implemented for ordinal/real valued variables
* @param a
* @param b
* @param cutoff
* @return
*/
public double distance(double[] a,double[] b, double cutoff){
// 1. Work out the ranges for each variable
if(ranges==null){
System.out.println("Error in Gower distance, ranges not calculated, exiting but should throw an exception!");
System.exit(0);
}
// 2.
//For all real attributes,
double dist=0;
for(int i=0;i<a.length;i++){
dist+=Math.abs(a[i]-b[i])/ranges[i];
}
return dist;
}
}