/// <summary> /// Partial least squares (PLS) decomposition of the matrizes X and Y. /// </summary> /// <param name="_X">The X ("spectrum") matrix, centered and preprocessed.</param> /// <param name="_Y">The Y ("concentration") matrix (centered).</param> /// <param name="numFactors">Number of factors to calculate.</param> /// <param name="xLoads">Returns the matrix of eigenvectors of X. Should be initially empty.</param> /// <param name="yLoads">Returns the matrix of eigenvectors of Y. Should be initially empty. </param> /// <param name="W">Returns the matrix of weighting values. Should be initially empty.</param> /// <param name="V">Returns the vector of cross products. Should be initially empty.</param> /// <param name="PRESS">If not null, the PRESS value of each factor is stored (vertically) here. </param> public static void ExecuteAnalysis( IROMatrix _X, // matrix of spectra (a spectra is a row of this matrix) IROMatrix _Y, // matrix of concentrations (a mixture is a row of this matrix) ref int numFactors, IBottomExtensibleMatrix xLoads, // out: the loads of the X matrix IBottomExtensibleMatrix yLoads, // out: the loads of the Y matrix IBottomExtensibleMatrix W, // matrix of weighting values IRightExtensibleMatrix V, // matrix of cross products IExtensibleVector PRESS //vector of Y PRESS values ) { // used variables: // n: number of spectra (number of tests, number of experiments) // p: number of slots (frequencies, ..) in each spectrum // m: number of constitutents (number of y values in each measurement) // X : n-p matrix of spectra (each spectra is a horizontal row) // Y : n-m matrix of concentrations const int maxIterations = 1500; // max number of iterations in one factorization step const double accuracy = 1E-12; // accuracy that should be reached between subsequent calculations of the u-vector // use the mean spectrum as first row of the W matrix MatrixMath.HorizontalVector mean = new MatrixMath.HorizontalVector(_X.Columns); // MatrixMath.ColumnsToZeroMean(X,mean); //W.AppendBottom(mean); IMatrix X = new MatrixMath.BEMatrix(_X.Rows,_X.Columns); MatrixMath.Copy(_X,X); IMatrix Y = new MatrixMath.BEMatrix(_Y.Rows,_Y.Columns); MatrixMath.Copy(_Y,Y); IMatrix u_prev = null; IMatrix w = new MatrixMath.HorizontalVector(X.Columns); // horizontal vector of X (spectral) weighting IMatrix t = new MatrixMath.VerticalVector(X.Rows); // vertical vector of X scores IMatrix u = new MatrixMath.VerticalVector(X.Rows); // vertical vector of Y scores IMatrix p = new MatrixMath.HorizontalVector(X.Columns); // horizontal vector of X loads IMatrix q = new MatrixMath.HorizontalVector(Y.Columns); // horizontal vector of Y loads int maxFactors = Math.Min(X.Columns,X.Rows); numFactors = numFactors<=0 ? maxFactors : Math.Min(numFactors,maxFactors); if(PRESS!=null) { PRESS.Append(new MatrixMath.Scalar(MatrixMath.SumOfSquares(Y))); // Press value for not decomposed Y } for(int nFactor=0; nFactor<numFactors; nFactor++) { //Console.WriteLine("Factor_{0}:",nFactor); //Console.WriteLine("X:"+X.ToString()); //Console.WriteLine("Y:"+Y.ToString()); // 1. Use as start vector for the y score the first column of the // y-matrix MatrixMath.Submatrix(Y,u); // u is now a vertical vector of concentrations of the first constituents for(int iter=0;iter<maxIterations;iter++) { // 2. Calculate the X (spectrum) weighting vector MatrixMath.MultiplyFirstTransposed(u,X,w); // w is a horizontal vector // 3. Normalize w to unit length MatrixMath.NormalizeRows(w); // w now has unit length // 4. Calculate X (spectral) scores MatrixMath.MultiplySecondTransposed(X,w,t); // t is a vertical vector of n numbers // 5. Calculate the Y (concentration) loading vector MatrixMath.MultiplyFirstTransposed(t,Y,q); // q is a horizontal vector of m (number of constitutents) // 5.1 Normalize q to unit length MatrixMath.NormalizeRows(q); // 6. Calculate the Y (concentration) score vector u MatrixMath.MultiplySecondTransposed(Y,q,u); // u is a vertical vector of n numbers // 6.1 Compare // Compare this with the previous one if(u_prev!=null && MatrixMath.IsEqual(u_prev,u,accuracy)) break; if(u_prev==null) u_prev = new MatrixMath.VerticalVector(X.Rows); MatrixMath.Copy(u,u_prev); // stores the content of u in u_prev } // for all iterations // Store the scores of X //factors.AppendRight(t); // 7. Calculate the inner scalar (cross product) double length_of_t = MatrixMath.LengthOf(t); MatrixMath.Scalar v = new MatrixMath.Scalar(0); MatrixMath.MultiplyFirstTransposed(u,t,v); if(length_of_t!=0) v = v/MatrixMath.Square(length_of_t); // 8. Calculate the new loads for the X (spectral) matrix MatrixMath.MultiplyFirstTransposed(t,X,p); // p is a horizontal vector of loads // Normalize p by the spectral scores if(length_of_t!=0) MatrixMath.MultiplyScalar(p,1/MatrixMath.Square(length_of_t),p); // 9. Calculate the new residua for the X (spectral) and Y (concentration) matrix //MatrixMath.MultiplyScalar(t,length_of_t*v,t); // original t times the cross product MatrixMath.SubtractProductFromSelf(t,p,X); MatrixMath.MultiplyScalar(t,v,t); // original t times the cross product MatrixMath.SubtractProductFromSelf(t,q,Y); // to calculate residual Y // Store the loads of X and Y in the output result matrix xLoads.AppendBottom(p); yLoads.AppendBottom(q); W.AppendBottom(w); V.AppendRight(v); if(PRESS!=null) { double pressValue=MatrixMath.SumOfSquares(Y); PRESS.Append(new MatrixMath.Scalar(pressValue)); } // Calculate SEPcv. If SEPcv is greater than for the actual number of factors, // break since the optimal number of factors was found. If not, repeat the calculations // with the residual matrizes for the next factor. } // for all factors }
/// <summary> /// Partial least squares (PLS) decomposition of the matrizes X and Y. /// </summary> /// <param name="_X">The X ("spectrum") matrix, centered and preprocessed.</param> /// <param name="_Y">The Y ("concentration") matrix (centered).</param> /// <param name="numFactors">Number of factors to calculate.</param> /// <param name="xLoads">Returns the matrix of eigenvectors of X. Should be initially empty.</param> /// <param name="yLoads">Returns the matrix of eigenvectors of Y. Should be initially empty. </param> /// <param name="W">Returns the matrix of weighting values. Should be initially empty.</param> /// <param name="V">Returns the vector of cross products. Should be initially empty.</param> /// <param name="PRESS">If not null, the PRESS value of each factor is stored (vertically) here. </param> public static void ExecuteAnalysis( IROMatrix _X, // matrix of spectra (a spectra is a row of this matrix) IROMatrix _Y, // matrix of concentrations (a mixture is a row of this matrix) ref int numFactors, IBottomExtensibleMatrix xLoads, // out: the loads of the X matrix IBottomExtensibleMatrix yLoads, // out: the loads of the Y matrix IBottomExtensibleMatrix W, // matrix of weighting values IRightExtensibleMatrix V, // matrix of cross products IExtensibleVector PRESS //vector of Y PRESS values ) { // used variables: // n: number of spectra (number of tests, number of experiments) // p: number of slots (frequencies, ..) in each spectrum // m: number of constitutents (number of y values in each measurement) // X : n-p matrix of spectra (each spectra is a horizontal row) // Y : n-m matrix of concentrations const int maxIterations = 1500; // max number of iterations in one factorization step const double accuracy = 1E-12; // accuracy that should be reached between subsequent calculations of the u-vector // use the mean spectrum as first row of the W matrix MatrixMath.HorizontalVector mean = new MatrixMath.HorizontalVector(_X.Columns); // MatrixMath.ColumnsToZeroMean(X,mean); //W.AppendBottom(mean); IMatrix X = new MatrixMath.BEMatrix(_X.Rows, _X.Columns); MatrixMath.Copy(_X, X); IMatrix Y = new MatrixMath.BEMatrix(_Y.Rows, _Y.Columns); MatrixMath.Copy(_Y, Y); IMatrix u_prev = null; IMatrix w = new MatrixMath.HorizontalVector(X.Columns); // horizontal vector of X (spectral) weighting IMatrix t = new MatrixMath.VerticalVector(X.Rows); // vertical vector of X scores IMatrix u = new MatrixMath.VerticalVector(X.Rows); // vertical vector of Y scores IMatrix p = new MatrixMath.HorizontalVector(X.Columns); // horizontal vector of X loads IMatrix q = new MatrixMath.HorizontalVector(Y.Columns); // horizontal vector of Y loads int maxFactors = Math.Min(X.Columns, X.Rows); numFactors = numFactors <= 0 ? maxFactors : Math.Min(numFactors, maxFactors); if (PRESS != null) { PRESS.Append(new MatrixMath.Scalar(MatrixMath.SumOfSquares(Y))); // Press value for not decomposed Y } for (int nFactor = 0; nFactor < numFactors; nFactor++) { //Console.WriteLine("Factor_{0}:",nFactor); //Console.WriteLine("X:"+X.ToString()); //Console.WriteLine("Y:"+Y.ToString()); // 1. Use as start vector for the y score the first column of the // y-matrix MatrixMath.Submatrix(Y, u); // u is now a vertical vector of concentrations of the first constituents for (int iter = 0; iter < maxIterations; iter++) { // 2. Calculate the X (spectrum) weighting vector MatrixMath.MultiplyFirstTransposed(u, X, w); // w is a horizontal vector // 3. Normalize w to unit length MatrixMath.NormalizeRows(w); // w now has unit length // 4. Calculate X (spectral) scores MatrixMath.MultiplySecondTransposed(X, w, t); // t is a vertical vector of n numbers // 5. Calculate the Y (concentration) loading vector MatrixMath.MultiplyFirstTransposed(t, Y, q); // q is a horizontal vector of m (number of constitutents) // 5.1 Normalize q to unit length MatrixMath.NormalizeRows(q); // 6. Calculate the Y (concentration) score vector u MatrixMath.MultiplySecondTransposed(Y, q, u); // u is a vertical vector of n numbers // 6.1 Compare // Compare this with the previous one if (u_prev != null && MatrixMath.IsEqual(u_prev, u, accuracy)) { break; } if (u_prev == null) { u_prev = new MatrixMath.VerticalVector(X.Rows); } MatrixMath.Copy(u, u_prev); // stores the content of u in u_prev } // for all iterations // Store the scores of X //factors.AppendRight(t); // 7. Calculate the inner scalar (cross product) double length_of_t = MatrixMath.LengthOf(t); MatrixMath.Scalar v = new MatrixMath.Scalar(0); MatrixMath.MultiplyFirstTransposed(u, t, v); if (length_of_t != 0) { v = v / MatrixMath.Square(length_of_t); } // 8. Calculate the new loads for the X (spectral) matrix MatrixMath.MultiplyFirstTransposed(t, X, p); // p is a horizontal vector of loads // Normalize p by the spectral scores if (length_of_t != 0) { MatrixMath.MultiplyScalar(p, 1 / MatrixMath.Square(length_of_t), p); } // 9. Calculate the new residua for the X (spectral) and Y (concentration) matrix //MatrixMath.MultiplyScalar(t,length_of_t*v,t); // original t times the cross product MatrixMath.SubtractProductFromSelf(t, p, X); MatrixMath.MultiplyScalar(t, v, t); // original t times the cross product MatrixMath.SubtractProductFromSelf(t, q, Y); // to calculate residual Y // Store the loads of X and Y in the output result matrix xLoads.AppendBottom(p); yLoads.AppendBottom(q); W.AppendBottom(w); V.AppendRight(v); if (PRESS != null) { double pressValue = MatrixMath.SumOfSquares(Y); PRESS.Append(new MatrixMath.Scalar(pressValue)); } // Calculate SEPcv. If SEPcv is greater than for the actual number of factors, // break since the optimal number of factors was found. If not, repeat the calculations // with the residual matrizes for the next factor. } // for all factors }
/// <summary> /// Calculates eigenvectors (loads) and the corresponding eigenvalues (scores) /// by means of the NIPALS algorithm /// </summary> /// <param name="X">The matrix to which the decomposition is applied to. A row of the matrix is one spectrum (or a single measurement giving multiple resulting values). The different rows of the matrix represent /// measurements under different conditions.</param> /// <param name="numFactors">The number of factors to be calculated. If 0 is provided, factors are calculated until the provided accuracy is reached. </param> /// <param name="accuracy">The relative residual variance that should be reached.</param> /// <param name="factors">Resulting matrix of factors. You have to provide a extensible matrix of dimension(0,0) as the vertical score vectors are appended to the matrix.</param> /// <param name="loads">Resulting matrix consiting of horizontal load vectors (eigenspectra). You have to provide a extensible matrix of dimension(0,0) here.</param> /// <param name="residualVarianceVector">Residual variance. Element[0] is the original variance, element[1] the residual variance after the first factor subtracted and so on. You can provide null if you don't need this result.</param> public static void NIPALS_HO( IMatrix X, int numFactors, double accuracy, IRightExtensibleMatrix factors, IBottomExtensibleMatrix loads, IBottomExtensibleMatrix residualVarianceVector) { // first center the matrix //MatrixMath.ColumnsToZeroMean(X, null); double originalVariance = Math.Sqrt(MatrixMath.SumOfSquares(X)); if(null!=residualVarianceVector) residualVarianceVector.AppendBottom(new MatrixMath.Scalar(originalVariance)); IMatrix l = new HorizontalVector(X.Columns); IMatrix t_prev = null; IMatrix t = new VerticalVector(X.Rows); int maxFactors = numFactors<=0 ? X.Columns : Math.Min(numFactors,X.Columns); for(int nFactor=0; nFactor<maxFactors; nFactor++) { //l has to be a horizontal vector // 1. Guess the transposed Vector l_transp, use first row of X matrix if it is not empty, otherwise the first non-empty row int rowoffset=0; do { Submatrix(X,l,rowoffset,0); // l is now a horizontal vector rowoffset++; } while(IsZeroMatrix(l) && rowoffset<X.Rows); for(int iter=0;iter<500;iter++) { // 2. Calculate the new vector t for the factor values MultiplySecondTransposed(X,l,t); // t = X*l_t (t is a vertical vector) // Compare this with the previous one if(t_prev!=null && IsEqual(t_prev,t,1E-9)) break; // 3. Calculate the new loads MultiplyFirstTransposed(t,X,l); // l = t_tr*X (gives a horizontal vector of load (= eigenvalue spectrum) // normalize the (one) row NormalizeRows(l); // normalize the eigenvector spectrum // 4. Goto step 2 or break after a number of iterations if(t_prev==null) t_prev = new VerticalVector(X.Rows); Copy(t,t_prev); // stores the content of t in t_prev } // Store factor and loads factors.AppendRight(t); loads.AppendBottom(l); // 5. Calculate the residual matrix X = X - t*l SubtractProductFromSelf(t,l,X); // X is now the residual matrix // if the number of factors to calculate is not provided, // calculate the norm of the residual matrix and compare with the original // one if(numFactors<=0 || null!=residualVarianceVector) { double residualVariance = Math.Sqrt(MatrixMath.SumOfSquares(X)); residualVarianceVector.AppendBottom(new MatrixMath.Scalar(residualVariance)); if(residualVariance<=accuracy*originalVariance) { break; } } } // for all factors } // end NIPALS