package weka.core.elastic_distance_measures; /* * Move Split Merge distance measure from @ARTICLE{stefan13move-split-merge, AUTHOR = "A. Stefan andf V. Athitsos and G. Das ", TITLE = "The Move-Split-Merge Metric for Time Series", JOURNAL = "{IEEE} TRANSACTIONS ON KNOWLEDGE AND DATA ENGINEERING", YEAR = "2013", VOLUME = "25 ", NUMBER = "6 ", PAGES="1425--1438" } * */ import weka.core.EuclideanDistance; import weka.core.Instance; import weka.core.neighboursearch.PerformanceStats; /** * * @author Chris Rimmer */ public class MSMDistance extends EuclideanDistance{ // c - cost of Split/Merge operation. Change this value to what is more // appropriate for your data. double c = 0.1; public MSMDistance(){ super(); this.m_DontNormalize = true; } public MSMDistance(double c){ super(); this.m_DontNormalize = true; this.c = c; } public void setC(double v){c=v;} /** * Distance method * * @param first instance 1 * @param second instance 2 * @param cutOffValue used for early abandon * @param stats * @return distance between instances */ @Override public double distance(Instance first, Instance second, double cutOffValue, PerformanceStats stats){ //Get the double arrays return distance(first,second,cutOffValue); } /** * distance method that converts instances to arrays of doubles * * @param first instance 1 * @param second instance 2 * @param cutOffValue used for early abandon * @return distance between instances */ @Override public double distance(Instance first, Instance second, double cutOffValue){ //remove class index from first instance if there is one int firtClassIndex = first.classIndex(); double[] arr1; if(firtClassIndex > 0){ arr1 = new double[first.numAttributes()-1]; for(int i = 0,j = 0; i < first.numAttributes(); i++){ if(i != firtClassIndex){ arr1[j]= first.value(i); j++; } } }else{ arr1 = first.toDoubleArray(); } //remove class index from second instance if there is one int secondClassIndex = second.classIndex(); double[] arr2; if(secondClassIndex > 0){ arr2 = new double[second.numAttributes()-1]; for(int i = 0,j = 0; i < second.numAttributes(); i++){ if(i != secondClassIndex){ arr2[j]= second.value(i); j++; } } }else{ arr2 = second.toDoubleArray(); } return distance(arr1,arr2,cutOffValue); } /** * calculates the distance between two instances (been converted to arrays) * Exact code from the authors downloaded from * http://omega.uta.edu/~athitsos/msm/ * * @param first instance 1 as array * @param second instance 2 as array * @param cutOffValue used for early abandon * @return distance between instances */ public double MSM_Distance(double[] X, double[] Y){ int m, n, i, j; m = X.length; n = Y.length; double Cost[][] = new double[m][n]; // Initialization Cost[0][0] = Math.abs(X[0] - Y[0]); for (i = 1; i< m; i++) { Cost[i][0] = Cost[i-1][0] + C(X[i], X[i-1], Y[0]); } for (j = 1; j < n; j++) { Cost[0][j] = Cost[0][j-1] + C(Y[j], X[0], Y[j-1]); } // Main Loop for( i = 1; i < m; i++){ for ( j = 1; j < n; j++){ double d1,d2, d3; d1 = Cost[i-1][j-1] + Math.abs( X[i] - Y[j] ); d2 = Cost[i-1][j] + C( X[i], X[i-1], Y[j]); d3 = Cost[i][j-1] + C( Y[j], X[i], Y[j-1]); Cost[i][j] = Math.min( d1, Math.min(d2,d3) ); } } // Output return Cost[m-1][n-1]; } public double C( double new_point, double x, double y){ double dist = 0; if ( ( (x <= new_point) && (new_point <= y) ) || ( (y <= new_point) && (new_point <= x) ) ) { dist = c; } else{ dist = c + Math.min( Math.abs(new_point - x) , Math.abs(new_point - y) ); } return dist; } public double distance(double[] first, double[] second, double cutOffValue){ return MSM_Distance(first,second); } }