28 RegisterClassifierModule< DTW > DTW::registerModule(
"DTW");
30 DTW::DTW(
bool useScaling,
bool useNullRejection,
double nullRejectionCoeff,UINT rejectionMode,
bool constrainWarpingPath,
double radius,
bool offsetUsingFirstSample,
bool useSmoothing,UINT smoothingFactor)
32 this->useScaling=useScaling;
33 this->useNullRejection = useNullRejection;
34 this->nullRejectionCoeff = nullRejectionCoeff;
35 this->rejectionMode = rejectionMode;
36 this->constrainWarpingPath = constrainWarpingPath;
37 this->radius = radius;
38 this->offsetUsingFirstSample = offsetUsingFirstSample;
39 this->useSmoothing = useSmoothing;
40 this->smoothingFactor = smoothingFactor;
42 supportsNullRejection =
true;
44 useZNormalisation=
false;
46 trimTrainingData =
false;
48 zNormConstrainThreshold=0.2;
50 maximumTrimPercentage = 90;
53 distanceMethod=EUCLIDEAN_DIST;
55 averageTemplateLength =0;
58 classifierType = classType;
59 classifierMode = TIMESERIES_CLASSIFIER_MODE;
60 debugLog.setProceedingText(
"[DEBUG NDDTW]");
61 errorLog.setProceedingText(
"[ERROR NDDTW]");
62 trainingLog.setProceedingText(
"[TRAINING NDDTW]");
63 warningLog.setProceedingText(
"[WARNING NDDTW]");
66 DTW::DTW(
const DTW &rhs){
78 this->templatesBuffer = rhs.templatesBuffer;
79 this->distanceMatrices = rhs.distanceMatrices;
80 this->warpPaths = rhs.warpPaths;
81 this->continuousInputDataBuffer = rhs.continuousInputDataBuffer;
82 this->numTemplates = rhs.numTemplates;
83 this->rejectionMode = rhs.rejectionMode;
84 this->useSmoothing = rhs.useSmoothing;
85 this->useZNormalisation = rhs.useZNormalisation;
86 this->constrainZNorm = rhs.constrainZNorm;
87 this->constrainWarpingPath = rhs.constrainWarpingPath;
88 this->trimTrainingData = rhs.trimTrainingData;
89 this->zNormConstrainThreshold = rhs.zNormConstrainThreshold;
90 this->radius = rhs.radius;
91 this->offsetUsingFirstSample = rhs.offsetUsingFirstSample;
92 this->trimThreshold = rhs.trimThreshold;
93 this->maximumTrimPercentage = rhs.maximumTrimPercentage;
94 this->smoothingFactor = rhs.smoothingFactor;
95 this->distanceMethod = rhs.distanceMethod;
96 this->rejectionMode = rhs.rejectionMode;
97 this->averageTemplateLength = rhs.averageTemplateLength;
107 if( classifier == NULL )
return false;
111 DTW *ptr = (
DTW*)classifier;
112 this->templatesBuffer = ptr->templatesBuffer;
113 this->distanceMatrices = ptr->distanceMatrices;
114 this->warpPaths = ptr->warpPaths;
115 this->continuousInputDataBuffer = ptr->continuousInputDataBuffer;
116 this->numTemplates = ptr->numTemplates;
117 this->rejectionMode = ptr->rejectionMode;
118 this->useSmoothing = ptr->useSmoothing;
119 this->useZNormalisation = ptr->useZNormalisation;
120 this->constrainZNorm = ptr->constrainZNorm;
121 this->constrainWarpingPath = ptr->constrainWarpingPath;
122 this->trimTrainingData = ptr->trimTrainingData;
123 this->zNormConstrainThreshold = ptr->zNormConstrainThreshold;
124 this->radius = ptr->radius;
125 this->offsetUsingFirstSample = ptr->offsetUsingFirstSample;
126 this->trimThreshold = ptr->trimThreshold;
127 this->maximumTrimPercentage = ptr->maximumTrimPercentage;
128 this->smoothingFactor = ptr->smoothingFactor;
129 this->distanceMethod = ptr->distanceMethod;
130 this->rejectionMode = ptr->rejectionMode;
131 this->averageTemplateLength = ptr->averageTemplateLength;
134 return copyBaseVariables( classifier );
145 templatesBuffer.clear();
148 continuousInputDataBuffer.clear();
150 if( trimTrainingData ){
156 if( timeSeriesTrimmer.
trimTimeSeries( labelledTrainingData[i] ) ){
157 tempData.
addSample(labelledTrainingData[i].getClassLabel(), labelledTrainingData[i].getData());
159 trainingLog <<
"Removing training sample " << i <<
" from the dataset as it could not be trimmed!" << endl;
163 labelledTrainingData = tempData;
167 errorLog <<
"train_(TimeSeriesClassificationData &labelledTrainingData) - Can't train model as there are no samples in training data!" << endl;
175 templatesBuffer.resize( numClasses );
176 classLabels.resize( numClasses );
177 nullRejectionThresholds.resize( numClasses );
178 averageTemplateLength = 0;
185 if( useScaling ) scaleData( trainingData );
186 if( useZNormalisation ) znormData( trainingData );
189 for(UINT k=0; k<numTemplates; k++){
197 templatesBuffer[k].classLabel = classLabel;
200 classLabels[k] = classLabel;
202 trainingLog <<
"Training Template: " << k <<
" Class: " << classLabel << endl;
205 if( numExamples < 1 ){
206 errorLog <<
"train_(TimeSeriesClassificationData &labelledTrainingData) - Can not train model: Num of Example is < 1! Class: " << classLabel <<
". Turn off null rejection if you want to use DTW with only 1 training sample per class." << endl;
210 if( numExamples == 1 && useNullRejection ){
211 errorLog <<
"train_(TimeSeriesClassificationData &labelledTrainingData) - Can not train model as there is only 1 example in class: " << classLabel <<
". Turn off null rejection if you want to use DTW with only 1 training sample per class." << endl;
215 if( numExamples == 1 ){
217 nullRejectionThresholds[k] = 0.0;
220 if( !train_NDDTW(classData,templatesBuffer[k],bestIndex) ){
221 errorLog <<
"train_(LabelledTimeSeriesClassificationData &labelledTrainingData) - Failed to train template for class with label: " << classLabel << endl;
227 int trainingMethod = 0;
228 if(useSmoothing) trainingMethod = 1;
230 switch (trainingMethod) {
232 templatesBuffer[k].timeSeries = classData[bestIndex].getData();
236 smoothData(classData[ bestIndex ].getData(),smoothingFactor,templatesBuffer[k].timeSeries);
239 cout<<
"Can not train model: Unknown training method \n";
244 if( offsetUsingFirstSample ){
245 offsetTimeseries( templatesBuffer[k].timeSeries );
249 averageTemplateLength += templatesBuffer[k].averageTemplateLength;
254 averageTemplateLength = averageTemplateLength/numTemplates;
257 recomputeNullRejectionThresholds();
260 continuousInputDataBuffer.clear();
261 continuousInputDataBuffer.resize(averageTemplateLength,vector<double>(numInputDimensions,0));
262 classLikelihoods.resize(numTemplates,DEFAULT_NULL_LIKELIHOOD_VALUE);
263 classDistances.resize(numTemplates,0);
264 predictedClassLabel = GRT_DEFAULT_NULL_CLASS_LABEL;
265 maxLikelihood = DEFAULT_NULL_LIKELIHOOD_VALUE;
274 VectorDouble results(numExamples,0.0);
276 dtwTemplate.averageTemplateLength = 0;
278 for(UINT m=0; m<numExamples; m++){
282 dtwTemplate.averageTemplateLength += trainingData[m].getLength();
285 if( useSmoothing ) smoothData(trainingData[m].getData(),smoothingFactor,templateA);
286 else templateA = trainingData[m].getData();
288 if( offsetUsingFirstSample ){
289 offsetTimeseries(templateA);
292 for(UINT n=0; n<numExamples; n++){
295 if( useSmoothing ) smoothData(trainingData[n].getData(),smoothingFactor,templateB);
296 else templateB = trainingData[n].getData();
298 if( offsetUsingFirstSample ){
299 offsetTimeseries(templateB);
304 vector< IndexDist > warpPath;
305 double dist = computeDistance(templateA,templateB,distanceMatrix,warpPath);
307 trainingLog <<
"Template: " << m <<
" Timeseries: " << n <<
" Dist: " << dist << endl;
310 distanceResults[m][n] = dist;
312 }
else distanceResults[m][n] = 0;
316 for(UINT m=0; m<numExamples; m++) results[m]/=(numExamples-1);
319 double bestAverage = results[0];
320 for(UINT m=1; m<numExamples; m++){
321 if( results[m] < bestAverage ){
322 bestAverage = results[m];
327 if( numExamples > 2 ){
330 dtwTemplate.trainingMu = results[bestIndex];
331 dtwTemplate.trainingSigma = 0.0;
333 for(UINT n=0; n<numExamples; n++){
335 dtwTemplate.trainingSigma += SQR( distanceResults[ bestIndex ][n] - dtwTemplate.trainingMu );
338 dtwTemplate.trainingSigma = sqrt( dtwTemplate.trainingSigma /
double(numExamples-2) );
340 warningLog <<
"_train_NDDTW(TimeSeriesClassificationData &trainingData,DTWTemplate &dtwTemplate,UINT &bestIndex - There are not enough examples to compute the trainingMu and trainingSigma for the template for class " << dtwTemplate.classLabel << endl;
341 dtwTemplate.trainingMu = 0.0;
342 dtwTemplate.trainingSigma = 0.0;
346 dtwTemplate.averageTemplateLength = (UINT) (dtwTemplate.averageTemplateLength/
double(numExamples));
348 trainingLog <<
"AverageTemplateLength: " << dtwTemplate.averageTemplateLength << endl;
358 errorLog <<
"predict_(MatrixDouble &inputTimeSeries) - The DTW templates have not been trained!" << endl;
362 if( classLikelihoods.size() != numTemplates ) classLikelihoods.resize(numTemplates);
363 if( classDistances.size() != numTemplates ) classDistances.resize(numTemplates);
365 predictedClassLabel = 0;
366 maxLikelihood = DEFAULT_NULL_LIKELIHOOD_VALUE;
367 for(UINT k=0; k<classLikelihoods.size(); k++){
368 classLikelihoods[k] = 0;
369 classDistances[k] = DEFAULT_NULL_LIKELIHOOD_VALUE;
372 if( numInputDimensions != inputTimeSeries.
getNumCols() ){
373 errorLog <<
"predict_(MatrixDouble &inputTimeSeries) - The number of features in the model (" << numInputDimensions <<
") do not match that of the input time series (" << inputTimeSeries.
getNumCols() <<
")" << endl;
382 scaleData(*timeSeriesPtr,processedTimeSeries);
383 timeSeriesPtr = &processedTimeSeries;
387 if( useZNormalisation ){
388 znormData(*timeSeriesPtr,processedTimeSeries);
389 timeSeriesPtr = &processedTimeSeries;
394 smoothData(*timeSeriesPtr,smoothingFactor,tempMatrix);
395 timeSeriesPtr = &tempMatrix;
399 if( offsetUsingFirstSample ){
400 offsetTimeseries( *timeSeriesPtr );
405 if( distanceMatrices.size() != numTemplates ) distanceMatrices.resize( numTemplates );
406 if( warpPaths.size() != numTemplates ) warpPaths.resize( numTemplates );
409 for(UINT k=0; k<numTemplates; k++){
411 classDistances[k] = computeDistance(templatesBuffer[k].timeSeries,*timeSeriesPtr,distanceMatrices[k],warpPaths[k]);
412 classLikelihoods[k] = 1.0 / classDistances[k];
413 sum += classLikelihoods[k];
417 UINT closestTemplateIndex = 0;
418 bestDistance = classDistances[0];
419 for(UINT k=1; k<numTemplates; k++){
420 if( classDistances[k] < bestDistance ){
421 bestDistance = classDistances[k];
422 closestTemplateIndex = k;
427 UINT maxLikelihoodIndex = 0;
430 for(UINT k=0; k<numTemplates; k++){
431 classLikelihoods[k] /= sum;
432 if( classLikelihoods[k] > maxLikelihood ){
433 maxLikelihood = classLikelihoods[k];
434 maxLikelihoodIndex = k;
439 if( useNullRejection ){
441 switch( rejectionMode ){
442 case TEMPLATE_THRESHOLDS:
443 if( bestDistance <= nullRejectionThresholds[ closestTemplateIndex ] ) predictedClassLabel = templatesBuffer[ closestTemplateIndex ].classLabel;
444 else predictedClassLabel = GRT_DEFAULT_NULL_CLASS_LABEL;
446 case CLASS_LIKELIHOODS:
447 if( maxLikelihood >= 0.99 ) predictedClassLabel = templatesBuffer[ maxLikelihoodIndex ].classLabel;
448 else predictedClassLabel = GRT_DEFAULT_NULL_CLASS_LABEL;
450 case THRESHOLDS_AND_LIKELIHOODS:
451 if( bestDistance <= nullRejectionThresholds[ closestTemplateIndex ] && maxLikelihood >= 0.99 )
452 predictedClassLabel = templatesBuffer[ closestTemplateIndex ].classLabel;
453 else predictedClassLabel = GRT_DEFAULT_NULL_CLASS_LABEL;
456 errorLog <<
"predict_(MatrixDouble &timeSeries) - Unknown RejectionMode!" << endl;
461 }
else predictedClassLabel = templatesBuffer[ closestTemplateIndex ].classLabel;
466 bool DTW::predict_(VectorDouble &inputVector){
469 errorLog <<
"predict_(VectorDouble &inputVector) - The model has not been trained!" << endl;
472 predictedClassLabel = 0;
473 maxLikelihood = DEFAULT_NULL_LIKELIHOOD_VALUE;
474 std::fill(classLikelihoods.begin(),classLikelihoods.end(),DEFAULT_NULL_LIKELIHOOD_VALUE);
475 std::fill(classDistances.begin(),classDistances.end(),0);
477 if( numInputDimensions != inputVector.size() ){
478 errorLog <<
"predict_(VectorDouble &inputVector) - The number of features in the model " << numInputDimensions <<
" does not match that of the input vector " << inputVector.size() << endl;
483 continuousInputDataBuffer.push_back( inputVector );
485 if( continuousInputDataBuffer.getNumValuesInBuffer() < averageTemplateLength ){
491 const UINT M = continuousInputDataBuffer.getSize();
492 const UINT N = numInputDimensions;
494 for(UINT i=0; i<M; i++){
495 for(UINT j=0; j<N; j++){
496 predictionTimeSeries[i][j] = continuousInputDataBuffer[i][j];
501 return predict( predictionTimeSeries );
506 continuousInputDataBuffer.clear();
508 continuousInputDataBuffer.resize(averageTemplateLength,vector<double>(numInputDimensions,0));
509 recomputeNullRejectionThresholds();
520 templatesBuffer.clear();
521 distanceMatrices.clear();
523 continuousInputDataBuffer.clear();
528 bool DTW::recomputeNullRejectionThresholds(){
530 if(!trained)
return false;
533 nullRejectionThresholds.resize(numTemplates);
535 for(UINT k=0; k<numTemplates; k++){
537 nullRejectionThresholds[k] = templatesBuffer[k].trainingMu + (templatesBuffer[k].trainingSigma * nullRejectionCoeff);
543 bool DTW::setModels( vector< DTWTemplate > newTemplates ){
545 if( newTemplates.size() == templatesBuffer.size() ){
546 templatesBuffer = newTemplates;
548 classLabels.resize( templatesBuffer.size() );
549 for(UINT i=0; i<templatesBuffer.size(); i++){
550 classLabels[i] = templatesBuffer[i].classLabel;
565 double totalDist,v,normFactor = 0.;
569 distanceMatrix.
resize(M, N);
572 switch (distanceMethod) {
573 case (ABSOLUTE_DIST):
576 distanceMatrix[i][j] = 0.0;
578 distanceMatrix[i][j] += fabs(timeSeriesA[i][k]-timeSeriesB[j][k]);
583 case (EUCLIDEAN_DIST):
587 distanceMatrix[i][j] = 0.0;
589 distanceMatrix[i][j] += SQR( timeSeriesA[i][k]-timeSeriesB[j][k] );
591 distanceMatrix[i][j] = sqrt( distanceMatrix[i][j] );
595 case (NORM_ABSOLUTE_DIST):
598 distanceMatrix[i][j] = 0.0;
600 distanceMatrix[i][j] += fabs(timeSeriesA[i][k]-timeSeriesB[j][k]);
602 distanceMatrix[i][j]/=N;
607 errorLog<<
"ERROR: Unknown distance method: "<<distanceMethod<<endl;
613 double distance = sqrt( d(M-1,N-1,distanceMatrix,M,N) );
615 if( grt_isinf(distance) || grt_isnan(distance) ){
616 warningLog <<
"DTW computeDistance(...) - Distance Matrix Values are INF!" << endl;
625 distanceMatrix[i][j] = fabs( distanceMatrix[i][j] );
632 totalDist = distanceMatrix[i][j];
633 warpPath.
push_back( IndexDist(i,j,distanceMatrix[i][j]) );
638 if( i==0 && j==0 )
break;
644 v = numeric_limits<double>::max();
646 if( distanceMatrix[i-1][j] < v ){ v = distanceMatrix[i-1][j]; index = 1; }
647 if( distanceMatrix[i][j-1] < v ){ v = distanceMatrix[i][j-1]; index = 2; }
648 if( distanceMatrix[i-1][j-1] <= v ){ index = 3; }
661 warningLog <<
"DTW computeDistance(...) - Could not compute a warping path for the input matrix! Dist: " << distanceMatrix[i-1][j] <<
" i: " << i <<
" j: " << j << endl;
668 totalDist += distanceMatrix[i][j];
669 warpPath.
push_back( IndexDist(i,j,distanceMatrix[i][j]) );
672 return totalDist/normFactor;
675 double DTW::d(
int m,
int n,MatrixDouble &distanceMatrix,
const int M,
const int N){
680 if( grt_isnan( distanceMatrix[m][n] ) ){
684 if( constrainWarpingPath ){
685 double r = ceil( min(M,N)*radius );
687 if( fabs( n-((N-1)/((M-1)/
double(m))) ) > r ){
688 if( n-((N-1)/((M-1)/
double(m))) > 0 ){
689 for(
int i=0; i<m; i++){
690 for(
int j=n; j<N; j++){
691 distanceMatrix[i][j] = NAN;
695 for(
int i=m; i<M; i++){
696 for(
int j=0; j<n; j++){
697 distanceMatrix[i][j] = NAN;
707 if( distanceMatrix[m][n] < 0 ){
708 dist = fabs( distanceMatrix[m][n] );
716 if( m == 0 && n == 0 ){
717 dist = distanceMatrix[0][0];
718 distanceMatrix[0][0] = -distanceMatrix[0][0];
725 double contribDist = d(m,n-1,distanceMatrix,M,N);
727 dist = distanceMatrix[m][n] + contribDist;
729 distanceMatrix[m][n] = -dist;
735 double contribDist = d(m-1,n,distanceMatrix,M,N);
737 dist = distanceMatrix[m][n] + contribDist;
739 distanceMatrix[m][n] = -dist;
743 double contribDist1 = d(m-1,n-1,distanceMatrix,M,N);
744 double contribDist2 = d(m-1,n,distanceMatrix,M,N);
745 double contribDist3 = d(m,n-1,distanceMatrix,M,N);
746 double minValue = numeric_limits<double>::max();
748 if( contribDist1 < minValue ){ minValue = contribDist1; index = 1; }
749 if( contribDist2 < minValue ){ minValue = contribDist2; index = 2; }
750 if( contribDist3 < minValue ){ minValue = contribDist3; index = 3; }
754 dist = distanceMatrix[m][n] + minValue;
757 dist = distanceMatrix[m][n] + minValue;
760 dist = distanceMatrix[m][n] + minValue;
767 distanceMatrix[m][n] = -dist;
776 inline double DTW::MIN_(
double a,
double b,
double c){
786 void DTW::scaleData(TimeSeriesClassificationData &trainingData){
789 for(UINT i=0; i<trainingData.getNumSamples(); i++){
790 scaleData( trainingData[i].getData(), trainingData[i].getData() );
795 void DTW::scaleData(MatrixDouble &data,MatrixDouble &scaledData){
797 const UINT R = data.getNumRows();
798 const UINT C = data.getNumCols();
800 if( scaledData.getNumRows() != R || scaledData.getNumCols() != C ){
801 scaledData.resize(R, C);
805 for(UINT i=0; i<R; i++)
806 for(UINT j=0; j<C; j++)
807 scaledData[i][j] = scale(data[i][j],ranges[j].minValue,ranges[j].maxValue,0.0,1.0);
811 void DTW::znormData(TimeSeriesClassificationData &trainingData){
813 for(UINT i=0; i<trainingData.getNumSamples(); i++){
814 znormData( trainingData[i].getData(), trainingData[i].getData() );
819 void DTW::znormData(MatrixDouble &data,MatrixDouble &normData){
821 const UINT R = data.getNumRows();
822 const UINT C = data.getNumCols();
824 if( normData.getNumRows() != R || normData.getNumCols() != C ){
825 normData.resize(R,C);
828 for(UINT j=0; j<C; j++){
833 for(UINT i=0; i<R; i++) mean += data[i][j];
837 for(UINT i=0; i<R; i++)
838 stdDev += SQR(data[i][j]-mean);
839 stdDev = sqrt( stdDev / (R - 1.0) );
841 if(constrainZNorm && stdDev < 0.01){
843 for(UINT i=0; i<R; i++)
844 normData[i][j] = (data[i][j] - mean);
847 for(UINT i=0; i<R; i++)
848 normData[i][j] = (data[i][j] - mean) / stdDev;
853 void DTW::smoothData(VectorDouble &data,UINT smoothFactor,VectorDouble &resultsData){
855 const UINT M = (UINT)data.size();
856 const UINT N = (UINT) floor(
double(M)/double(smoothFactor));
857 resultsData.resize(N,0);
858 for(UINT i=0; i<N; i++) resultsData[i]=0.0;
860 if(smoothFactor==1 || M<smoothFactor){
865 for(UINT i=0; i<N; i++){
867 UINT index = i*smoothFactor;
868 for(UINT x=0; x<smoothFactor; x++){
869 mean += data[index+x];
871 resultsData[i] = mean/smoothFactor;
874 if(M%smoothFactor!=0.0){
876 for(UINT i=N*smoothFactor; i<M; i++) mean += data[i];
877 mean/=M-(N*smoothFactor);
879 VectorDouble tempVector(N+1);
880 for(UINT i=0; i<N; i++) tempVector[i] = resultsData[i];
881 tempVector[N] = mean;
882 resultsData = tempVector;
887 void DTW::smoothData(MatrixDouble &data,UINT smoothFactor,MatrixDouble &resultsData){
889 const UINT M = data.getNumRows();
890 const UINT C = data.getNumCols();
891 const UINT N = (UINT) floor(
double(M)/double(smoothFactor));
892 resultsData.resize(N,C);
894 if(smoothFactor==1 || M<smoothFactor){
899 for(UINT i=0; i<N; i++){
900 for(UINT j=0; j<C; j++){
902 int index = i*smoothFactor;
903 for(UINT x=0; x<smoothFactor; x++){
904 mean += data[index+x][j];
906 resultsData[i][j] = mean/smoothFactor;
911 if(M%smoothFactor!=0.0){
912 VectorDouble mean(C,0.0);
913 for(UINT j=0; j<C; j++){
914 for(UINT i=N*smoothFactor; i<M; i++) mean[j] += data[i][j];
915 mean[j]/=M-(N*smoothFactor);
919 MatrixDouble tempMatrix(N+1,C);
921 for(UINT i=0; i<N; i++)
922 for(UINT j=0; j<C; j++)
923 tempMatrix[i][j] = resultsData[i][j];
925 for(UINT j=0; j<C; j++) tempMatrix[N][j] = mean[j];
926 resultsData = tempMatrix;
933 bool DTW::saveModelToFile( fstream &file )
const{
936 errorLog <<
"saveModelToFile( string fileName ) - Could not open file to save data" << endl;
940 file <<
"GRT_DTW_Model_File_V2.0" <<endl;
943 if( !Classifier::saveBaseSettingsToFile(file) ){
944 errorLog <<
"saveModelToFile(fstream &file) - Failed to save classifier base settings to file!" << endl;
948 file <<
"DistanceMethod: ";
949 switch(distanceMethod){
951 file <<ABSOLUTE_DIST<<endl;
953 case(EUCLIDEAN_DIST):
954 file <<EUCLIDEAN_DIST<<endl;
957 file <<ABSOLUTE_DIST<<endl;
960 file <<
"UseSmoothing: "<<useSmoothing<<endl;
961 file <<
"SmoothingFactor: "<<smoothingFactor<<endl;
962 file <<
"UseZNormalisation: "<<useZNormalisation<<endl;
963 file <<
"OffsetUsingFirstSample: " << offsetUsingFirstSample << endl;
964 file <<
"ConstrainWarpingPath: " << constrainWarpingPath << endl;
965 file <<
"Radius: " << radius << endl;
966 file <<
"RejectionMode: " << rejectionMode<< endl;
969 file <<
"NumberOfTemplates: " << numTemplates << endl;
970 file <<
"OverallAverageTemplateLength: " << averageTemplateLength << endl;
972 for(UINT i=0; i<numTemplates; i++){
973 file <<
"***************TEMPLATE***************" << endl;
974 file <<
"Template: " << i+1 << endl;
975 file <<
"ClassLabel: " << templatesBuffer[i].classLabel << endl;
976 file <<
"TimeSeriesLength: " << templatesBuffer[i].timeSeries.getNumRows() << endl;
977 file <<
"TemplateThreshold: " << nullRejectionThresholds[i] << endl;
978 file <<
"TrainingMu: " << templatesBuffer[i].trainingMu << endl;
979 file <<
"TrainingSigma: " << templatesBuffer[i].trainingSigma << endl;
980 file <<
"AverageTemplateLength: " << templatesBuffer[i].averageTemplateLength << endl;
981 file <<
"TimeSeries: " << endl;
982 for(UINT k=0; k<templatesBuffer[i].timeSeries.getNumRows(); k++){
983 for(UINT j=0; j<templatesBuffer[i].timeSeries.getNumCols(); j++){
984 file << templatesBuffer[i].timeSeries[k][j] <<
"\t";
994 bool DTW::loadModelFromFile( fstream &file ){
997 UINT timeSeriesLength;
1002 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to open file!" << endl;
1009 if( word ==
"GRT_DTW_Model_File_V1.0" ){
1010 return loadLegacyModelFromFile( file );
1014 if(word !=
"GRT_DTW_Model_File_V2.0"){
1015 errorLog <<
"loadDTWModelFromFile( string fileName ) - Unknown file header!" << endl;
1020 if( !Classifier::loadBaseSettingsFromFile(file) ){
1021 errorLog <<
"loadModelFromFile(string filename) - Failed to load base settings from file!" << endl;
1027 if(word !=
"DistanceMethod:"){
1028 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find DistanceMethod!" << endl;
1031 file >> distanceMethod;
1035 if(word !=
"UseSmoothing:"){
1036 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find UseSmoothing!" << endl;
1039 file >> useSmoothing;
1043 if(word !=
"SmoothingFactor:"){
1044 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find SmoothingFactor!" << endl;
1047 file >> smoothingFactor;
1051 if(word !=
"UseZNormalisation:"){
1052 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find UseZNormalisation!" << endl;
1055 file >> useZNormalisation;
1059 if(word !=
"OffsetUsingFirstSample:"){
1060 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find OffsetUsingFirstSample!" << endl;
1063 file >> offsetUsingFirstSample;
1067 if(word !=
"ConstrainWarpingPath:"){
1068 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find ConstrainWarpingPath!" << endl;
1071 file >> constrainWarpingPath;
1075 if(word !=
"Radius:"){
1076 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find Radius!" << endl;
1083 if(word !=
"RejectionMode:"){
1084 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find RejectionMode!" << endl;
1087 file >> rejectionMode;
1093 if(word !=
"NumberOfTemplates:"){
1094 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find NumberOfTemplates!" << endl;
1097 file >> numTemplates;
1101 if(word !=
"OverallAverageTemplateLength:"){
1102 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find OverallAverageTemplateLength!" << endl;
1105 file >> averageTemplateLength;
1108 templatesBuffer.resize(numTemplates);
1109 classLabels.resize(numTemplates);
1110 nullRejectionThresholds.resize(numTemplates);
1113 for(UINT i=0; i<numTemplates; i++){
1116 if( word !=
"***************TEMPLATE***************" ){
1118 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find template header!" << endl;
1124 if(word !=
"Template:"){
1126 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find Template Number!" << endl;
1134 errorLog <<
"loadDTWModelFromFile( string fileName ) - Invalid Template Number: " << ts << endl;
1140 if(word !=
"ClassLabel:"){
1142 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find ClassLabel!" << endl;
1145 file >> templatesBuffer[i].classLabel;
1146 classLabels[i] = templatesBuffer[i].classLabel;
1150 if(word !=
"TimeSeriesLength:"){
1152 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find TimeSeriesLength!" << endl;
1155 file >> timeSeriesLength;
1158 templatesBuffer[i].timeSeries.resize(timeSeriesLength,numInputDimensions);
1162 if(word !=
"TemplateThreshold:"){
1164 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find TemplateThreshold!" << endl;
1167 file >> nullRejectionThresholds[i];
1171 if(word !=
"TrainingMu:"){
1173 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find TrainingMu!" << endl;
1176 file >> templatesBuffer[i].trainingMu;
1180 if(word !=
"TrainingSigma:"){
1182 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find TrainingSigma!" << endl;
1185 file >> templatesBuffer[i].trainingSigma;
1189 if(word !=
"AverageTemplateLength:"){
1191 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find AverageTemplateLength!" << endl;
1194 file >> templatesBuffer[i].averageTemplateLength;
1198 if(word !=
"TimeSeries:"){
1200 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find template timeseries!" << endl;
1203 for(UINT k=0; k<timeSeriesLength; k++)
1204 for(UINT j=0; j<numInputDimensions; j++)
1205 file >> templatesBuffer[i].timeSeries[k][j];
1209 continuousInputDataBuffer.clear();
1210 continuousInputDataBuffer.resize(averageTemplateLength,vector<double>(numInputDimensions,0));
1211 maxLikelihood = DEFAULT_NULL_LIKELIHOOD_VALUE;
1212 bestDistance = DEFAULT_NULL_DISTANCE_VALUE;
1213 classLikelihoods.resize(numClasses,DEFAULT_NULL_LIKELIHOOD_VALUE);
1214 classDistances.resize(numClasses,DEFAULT_NULL_DISTANCE_VALUE);
1219 bool DTW::setRejectionMode(UINT rejectionMode){
1220 if( rejectionMode == TEMPLATE_THRESHOLDS || rejectionMode == CLASS_LIKELIHOODS || rejectionMode == THRESHOLDS_AND_LIKELIHOODS ){
1221 this->rejectionMode = rejectionMode;
1227 bool DTW::setOffsetTimeseriesUsingFirstSample(
bool offsetUsingFirstSample){
1228 this->offsetUsingFirstSample = offsetUsingFirstSample;
1232 bool DTW::setContrainWarpingPath(
bool constrain){
1233 this->constrainWarpingPath = constrain;
1237 bool DTW::setWarpingRadius(
double radius){
1238 this->radius = radius;
1242 bool DTW::enableZNormalization(
bool useZNormalisation,
bool constrainZNorm){
1243 this->useZNormalisation = useZNormalisation;
1244 this->constrainZNorm = constrainZNorm;
1248 bool DTW::enableTrimTrainingData(
bool trimTrainingData,
double trimThreshold,
double maximumTrimPercentage){
1250 if( trimThreshold < 0 || trimThreshold > 1 ){
1251 warningLog <<
"Failed to set trimTrainingData. The trimThreshold must be in the range of [0 1]" << endl;
1254 if( maximumTrimPercentage < 0 || maximumTrimPercentage > 100 ){
1255 warningLog <<
"Failed to set trimTrainingData. The maximumTrimPercentage must be a valid percentage in the range of [0 100]" << endl;
1259 this->trimTrainingData = trimTrainingData;
1260 this->trimThreshold = trimThreshold;
1261 this->maximumTrimPercentage = maximumTrimPercentage;
1267 for(UINT i=0; i<timeseries.
getNumRows(); i++){
1268 for(UINT j=0; j<timeseries.
getNumCols(); j++){
1269 timeseries[i][j] -= firstRow[j];
1274 bool DTW::loadLegacyModelFromFile( fstream &file ){
1277 UINT timeSeriesLength;
1282 if(word !=
"NumberOfDimensions:"){
1283 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find NumberOfDimensions!" << endl;
1286 file >> numInputDimensions;
1290 if(word !=
"NumberOfClasses:"){
1291 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find NumberOfClasses!" << endl;
1298 if(word !=
"NumberOfTemplates:"){
1299 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find NumberOfTemplates!" << endl;
1302 file >> numTemplates;
1306 if(word !=
"DistanceMethod:"){
1307 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find DistanceMethod!" << endl;
1310 file >> distanceMethod;
1314 if(word !=
"UseNullRejection:"){
1315 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find UseNullRejection!" << endl;
1318 file >> useNullRejection;
1322 if(word !=
"UseSmoothing:"){
1323 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find UseSmoothing!" << endl;
1326 file >> useSmoothing;
1330 if(word !=
"SmoothingFactor:"){
1331 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find SmoothingFactor!" << endl;
1334 file >> smoothingFactor;
1338 if(word !=
"UseScaling:"){
1339 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find UseScaling!" << endl;
1346 if(word !=
"UseZNormalisation:"){
1347 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find UseZNormalisation!" << endl;
1350 file >> useZNormalisation;
1354 if(word !=
"OffsetUsingFirstSample:"){
1355 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find OffsetUsingFirstSample!" << endl;
1358 file >> offsetUsingFirstSample;
1362 if(word !=
"ConstrainWarpingPath:"){
1363 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find ConstrainWarpingPath!" << endl;
1366 file >> constrainWarpingPath;
1370 if(word !=
"Radius:"){
1371 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find Radius!" << endl;
1378 if(word !=
"RejectionMode:"){
1379 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find RejectionMode!" << endl;
1382 file >> rejectionMode;
1386 if(word !=
"NullRejectionCoeff:"){
1387 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find NullRejectionCoeff!" << endl;
1390 file >> nullRejectionCoeff;
1394 if(word !=
"OverallAverageTemplateLength:"){
1395 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find OverallAverageTemplateLength!" << endl;
1398 file >> averageTemplateLength;
1401 templatesBuffer.
resize(numTemplates);
1402 classLabels.resize(numTemplates);
1403 nullRejectionThresholds.resize(numTemplates);
1406 for(UINT i=0; i<numTemplates; i++){
1409 while(word !=
"Template:"){
1418 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find Invalid Template Number!" << endl;
1424 if(word !=
"ClassLabel:"){
1427 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find ClassLabel!" << endl;
1430 file >> templatesBuffer[i].classLabel;
1431 classLabels[i] = templatesBuffer[i].classLabel;
1435 if(word !=
"TimeSeriesLength:"){
1438 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find TimeSeriesLength!" << endl;
1441 file >> timeSeriesLength;
1444 templatesBuffer[i].timeSeries.resize(timeSeriesLength,numInputDimensions);
1448 if(word !=
"TemplateThreshold:"){
1451 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find TemplateThreshold!" << endl;
1454 file >> nullRejectionThresholds[i];
1458 if(word !=
"TrainingMu:"){
1461 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find TrainingMu!" << endl;
1464 file >> templatesBuffer[i].trainingMu;
1468 if(word !=
"TrainingSigma:"){
1471 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find TrainingSigma!" << endl;
1474 file >> templatesBuffer[i].trainingSigma;
1478 if(word !=
"AverageTemplateLength:"){
1481 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find AverageTemplateLength!" << endl;
1484 file >> templatesBuffer[i].averageTemplateLength;
1488 if(word !=
"TimeSeries:"){
1491 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find template timeseries!" << endl;
1494 for(UINT k=0; k<timeSeriesLength; k++)
1495 for(UINT j=0; j<numInputDimensions; j++)
1496 file >> templatesBuffer[i].timeSeries[k][j];
1500 if(word !=
"***************************"){
1503 numInputDimensions=0;
1505 errorLog <<
"loadDTWModelFromFile( string fileName ) - Failed to find template footer!" << endl;
1511 continuousInputDataBuffer.clear();
1512 continuousInputDataBuffer.resize(averageTemplateLength,vector<double>(numInputDimensions,0));
1513 maxLikelihood = DEFAULT_NULL_LIKELIHOOD_VALUE;
1514 bestDistance = DEFAULT_NULL_DISTANCE_VALUE;
1515 classLikelihoods.resize(numClasses,DEFAULT_NULL_LIKELIHOOD_VALUE);
1516 classDistances.resize(numClasses,DEFAULT_NULL_DISTANCE_VALUE);
bool trimTimeSeries(TimeSeriesClassificationSample &timeSeries)
UINT getNumSamples() const
UINT getNumDimensions() const
bool push_back(const std::vector< T > &sample)
unsigned int getNumCols() const
bool setNumDimensions(const UINT numDimensions)
bool addSample(const UINT classLabel, const MatrixDouble &trainingSample)
UINT getNumClasses() const
This class implements Dynamic Time Warping. Dynamic Time Warping (DTW) is a powerful classifier that ...
vector< ClassTracker > getClassTracker() const
TimeSeriesClassificationData getClassData(const UINT classLabel) const
unsigned int getNumRows() const
string getClassifierType() const
std::vector< T > getRowVector(const unsigned int r) const
vector< MinMax > getRanges() const
virtual bool resize(const unsigned int r, const unsigned int c)