/// <summary> /// Applies the rotation operator to the given dataset according to the reference dataset /// </summary> /// <param name="p">Procrusted dataset to rotate</param> /// <param name="p_reference">Reference procrusted dataset</param> /// <returns>The rotated dataset</returns> double[,] Rotate(ProcrustedDataset p, ProcrustedDataset p_reference) { // Rotation calculus per Amy Ross, Procrustes Analysis : http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.119.2686&rep=rep1&type=pdf SingularValueDecomposition svd = new SingularValueDecomposition(p_reference.Dataset.TransposeAndDot(p.Dataset)); double[,] Q = svd.RightSingularVectors.TransposeAndDot(svd.LeftSingularVectors); p.RotationMatrix = Q; return(p.Dataset.Dot(Q)); }
/// <summary> /// Apply Procrustes translation and scale to the given dataset /// </summary> /// /// <param name="p">Procrusted dataset to process and store the results to</param> /// /// <param name="samples">The dataset itself</param> /// private void ApplyTranslateScale(ProcrustedDataset p, double[,] samples) { // Save the original data p.Source = samples; // Save the original data center (i.e. mean) p.Center = p.Source.Mean(dimension: 0); // Translate the samples to zero p.Dataset = Translate(samples); // Save the original scale of the dataset p.Scale = GetDatasetScale(p.Dataset); // Scale the dataset to 1 p.Dataset = Scale(p.Dataset); }
/// <summary> /// Transforms the dataset to match the given reference original dataset /// </summary> /// <param name="p_reference">Dataset to match</param> /// <returns>The transformed dataset matched to the reference</returns> public double[,] Transform(ProcrustedDataset p_reference) { // Make a copy of the current Procrustes dataset double[,] tData = (double[, ])Dataset.Clone(); // Rotate the dataset to match the reference dataset rotation tData = tData.Dot(p_reference.RotationMatrix.Transpose()); // Scale the dataset to match the reference scale tData = tData.Multiply(p_reference.Scale); // Prepare a negative translation vector to... double[] refCenter = p_reference.Center.Multiply(-1); // ... move the dataset to the same center as the reference tData = tData.Center(refCenter); return(tData); }
/// <summary> /// Compute the Procrustes analysis to extract Procrustes distances and models by specifying the reference dataset /// </summary> /// /// <param name="reference_sample_index">Index of the reference dataset. If out of bounds of the sample array, the first dataset is used.</param> /// <param name="samples">List of sample data sets to analyze</param> /// /// <returns>Procrustes distances of the analyzed samples</returns> /// public double[,] Compute(int reference_sample_index, params double[][,] samples) { // Check arguments in case of mistakes CheckSampleDataArgument(samples); // If reference index out of bounds... if (reference_sample_index < 0 || reference_sample_index >= samples.Length) { // Use the first element of the array reference_sample_index = 0; } // Allocate space for the computed results ProcrustedDatasets = new ProcrustedDataset[samples.Length]; // Start with the reference dataset ProcrustedDatasets[reference_sample_index] = new ProcrustedDataset(); // The rotation martrix of the reference is the identity matrix (i.e. no rotation since it's the reference itself) ProcrustedDatasets[reference_sample_index].RotationMatrix = Matrix.Identity(samples[reference_sample_index].GetLength(1)); // Translate then scale the dataset to set it with scale = 1 and center position at zero ApplyTranslateScale(ProcrustedDatasets[reference_sample_index], samples[reference_sample_index]); // For each data set for (int i = 0; i < samples.Length; i++) { // Except for the reference if (i != reference_sample_index) { ProcrustedDatasets[i] = new ProcrustedDataset(); // Translate then scale the dataset ApplyTranslateScale(ProcrustedDatasets[i], samples[i]); // Finally, rotate the sample to fit the reference data rotation ProcrustedDatasets[i].Dataset = Rotate(ProcrustedDatasets[i], ProcrustedDatasets[reference_sample_index]); } } // Update the Procrustes distance matrix UpdateProcrustesDistances(ProcrustedDatasets); return(ProcrustesDistances); }
/// <summary> /// Transforms the dataset to match the given reference original dataset /// </summary> /// <param name="p_reference">Dataset to match</param> /// <returns>The transformed dataset matched to the reference</returns> public double[,] Transform(ProcrustedDataset p_reference) { // Make a copy of the current Procrustes dataset double[,] tData = (double[,])Dataset.Clone(); // Rotate the dataset to match the reference dataset rotation tData = tData.Dot(p_reference.RotationMatrix.Transpose()); // Scale the dataset to match the reference scale tData = tData.Multiply(p_reference.Scale); // Prepare a negative translation vector to... double[] refCenter = p_reference.Center.Multiply(-1); // ... move the dataset to the same center as the reference tData = tData.Center(refCenter); return tData; }
/// <summary> /// Compute the Procrustes analysis to extract Procrustes distances and models by specifying the reference dataset /// </summary> /// /// <param name="reference_sample_index">Index of the reference dataset. If out of bounds of the sample array, the first dataset is used.</param> /// <param name="samples">List of sample data sets to analyze</param> /// /// <returns>Procrustes distances of the analyzed samples</returns> /// public double[,] Compute(int reference_sample_index, params double[][,] samples) { // Check arguments in case of mistakes CheckSampleDataArgument(samples); // If reference index out of bounds... if (reference_sample_index < 0 || reference_sample_index >= samples.Length) { // Use the first element of the array reference_sample_index = 0; } // Allocate space for the computed results ProcrustedDatasets = new ProcrustedDataset[samples.Length]; // Start with the reference dataset ProcrustedDatasets[reference_sample_index] = new ProcrustedDataset(); // The rotation martrix of the reference is the identity matrix (i.e. no rotation since it's the reference itself) ProcrustedDatasets[reference_sample_index].RotationMatrix = Matrix.Identity(samples[reference_sample_index].GetLength(1)); // Translate then scale the dataset to set it with scale = 1 and center position at zero ApplyTranslateScale(ProcrustedDatasets[reference_sample_index], samples[reference_sample_index]); // For each data set for (int i = 0; i < samples.Length; i++) { // Except for the reference if (i != reference_sample_index) { ProcrustedDatasets[i] = new ProcrustedDataset(); // Translate then scale the dataset ApplyTranslateScale(ProcrustedDatasets[i], samples[i]); // Finally, rotate the sample to fit the reference data rotation ProcrustedDatasets[i].Dataset = Rotate(ProcrustedDatasets[i], ProcrustedDatasets[reference_sample_index]); } } // Update the Procrustes distance matrix UpdateProcrustesDistances(ProcrustedDatasets); return ProcrustesDistances; }
/// <summary> /// Applies the rotation operator to the given dataset according to the reference dataset /// </summary> /// <param name="p">Procrusted dataset to rotate</param> /// <param name="p_reference">Reference procrusted dataset</param> /// <returns>The rotated dataset</returns> double[,] Rotate(ProcrustedDataset p, ProcrustedDataset p_reference) { // Rotation calculus per Amy Ross, Procrustes Analysis : http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.119.2686&rep=rep1&type=pdf SingularValueDecomposition svd = new SingularValueDecomposition(p_reference.Dataset.TransposeAndDot(p.Dataset)); double[,] Q = svd.RightSingularVectors.TransposeAndDot(svd.LeftSingularVectors); p.RotationMatrix = Q; return p.Dataset.Dot(Q); }