/// <summary> /// Multiplies selected columns to form a matrix. /// </summary> /// <param name="mainDocument"></param> /// <param name="srctable"></param> /// <param name="selectedColumns"></param> /// <returns>Null if successful, else the description of the error.</returns> /// <remarks>The user must select an even number of columns. All columns of the first half of the selection /// must have the same number of rows, and all columns of the second half of selection must also have the same /// number of rows. The first half of selected columns form a matrix of dimensions(firstrowcount,halfselected), and the second half /// of selected columns form a matrix of dimension(halfselected, secondrowcount). The resulting matrix has dimensions (firstrowcount,secondrowcount) and is /// stored in a separate worksheet.</remarks> public static string MultiplyColumnsToMatrix( Altaxo.AltaxoDocument mainDocument, Altaxo.Data.DataTable srctable, IAscendingIntegerCollection selectedColumns ) { // check that there are columns selected if(0==selectedColumns.Count) return "You must select at least two columns to multiply!"; // selected columns must contain an even number of columns if(0!=selectedColumns.Count%2) return "You selected an odd number of columns. Please select an even number of columns to multiply!"; // all selected columns must be numeric columns for(int i=0;i<selectedColumns.Count;i++) { if(!(srctable[selectedColumns[i]] is Altaxo.Data.INumericColumn)) return string.Format("The column[{0}] (name:{1}) is not a numeric column!",selectedColumns[i],srctable[selectedColumns[i]].Name); } int halfselect = selectedColumns.Count/2; // check that all columns from the first half of selected colums contain the same // number of rows int rowsfirsthalf=int.MinValue; for(int i=0;i<halfselect;i++) { int idx = selectedColumns[i]; if(rowsfirsthalf<0) rowsfirsthalf = srctable[idx].Count; else if(rowsfirsthalf != srctable[idx].Count) return "The first half of selected columns have not all the same length!"; } int rowssecondhalf=int.MinValue; for(int i=halfselect;i<selectedColumns.Count;i++) { int idx = selectedColumns[i]; if(rowssecondhalf<0) rowssecondhalf = srctable[idx].Count; else if(rowssecondhalf != srctable[idx].Count) return "The second half of selected columns have not all the same length!"; } // now create the matrices to multiply from the MatrixMath.REMatrix firstMat = new MatrixMath.REMatrix(rowsfirsthalf,halfselect); for(int i=0;i<halfselect;i++) { Altaxo.Data.INumericColumn col = (Altaxo.Data.INumericColumn)srctable[selectedColumns[i]]; for(int j=0;j<rowsfirsthalf;j++) firstMat[j,i] = col[j]; } MatrixMath.BEMatrix secondMat = new MatrixMath.BEMatrix(halfselect,rowssecondhalf); for(int i=0;i<halfselect;i++) { Altaxo.Data.INumericColumn col = (Altaxo.Data.INumericColumn)srctable[selectedColumns[i+halfselect]]; for(int j=0;j<rowssecondhalf;j++) secondMat[i,j] = col[j]; } // now multiply the two matrices MatrixMath.BEMatrix resultMat = new MatrixMath.BEMatrix(rowsfirsthalf,rowssecondhalf); MatrixMath.Multiply(firstMat,secondMat,resultMat); // and store the result in a new worksheet Altaxo.Data.DataTable table = new Altaxo.Data.DataTable("ResultMatrix of " + srctable.Name); table.Suspend(); // first store the factors for(int i=0;i<resultMat.Columns;i++) { Altaxo.Data.DoubleColumn col = new Altaxo.Data.DoubleColumn(); for(int j=0;j<resultMat.Rows;j++) col[j] = resultMat[j,i]; table.DataColumns.Add(col,i.ToString()); } table.Resume(); mainDocument.DataTableCollection.Add(table); // create a new worksheet without any columns Current.ProjectService.CreateNewWorksheet(table); return null; }
/// <summary> /// Creates an analyis from preprocessed spectra and preprocessed concentrations. /// </summary> /// <param name="matrixX">The spectral matrix (each spectrum is a row in the matrix). They must at least be centered.</param> /// <param name="matrixY">The matrix of concentrations (each experiment is a row in the matrix). They must at least be centered.</param> /// <param name="maxFactors">Maximum number of factors for analysis.</param> /// <returns>A regression object, which holds all the loads and weights neccessary for further calculations.</returns> protected override void AnalyzeFromPreprocessedWithoutReset(IROMatrix matrixX, IROMatrix matrixY, int maxFactors) { int numberOfFactors = _calib.NumberOfFactors = Math.Min(matrixX.Columns, maxFactors); MatrixMath.BEMatrix _xLoads = new MatrixMath.BEMatrix(0,0); MatrixMath.BEMatrix _yLoads = new MatrixMath.BEMatrix(0,0); MatrixMath.BEMatrix _W = new MatrixMath.BEMatrix(0,0); MatrixMath.REMatrix _V = new MatrixMath.REMatrix(0,0); _PRESS = VectorMath.CreateExtensibleVector(0); ExecuteAnalysis(matrixX, matrixY, ref numberOfFactors, _xLoads, _yLoads, _W, _V, _PRESS); _calib.NumberOfFactors = Math.Min(_calib.NumberOfFactors,numberOfFactors); _calib.XLoads = _xLoads; _calib.YLoads = _yLoads; _calib.XWeights = _W; _calib.CrossProduct = _V; }
/// <summary> /// Makes a PCA (a principal component analysis) of the table or the selected columns / rows and stores the results in a newly created table. /// </summary> /// <param name="mainDocument">The main document of the application.</param> /// <param name="srctable">The table where the data come from.</param> /// <param name="selectedColumns">The selected columns.</param> /// <param name="selectedRows">The selected rows.</param> /// <param name="bHorizontalOrientedSpectrum">True if a spectrum is a single row, False if a spectrum is a single column.</param> /// <param name="maxNumberOfFactors">The maximum number of factors to calculate.</param> /// <returns></returns> public static string PrincipalComponentAnalysis( Altaxo.AltaxoDocument mainDocument, Altaxo.Data.DataTable srctable, IAscendingIntegerCollection selectedColumns, IAscendingIntegerCollection selectedRows, bool bHorizontalOrientedSpectrum, int maxNumberOfFactors ) { bool bUseSelectedColumns = (null!=selectedColumns && 0!=selectedColumns.Count); int prenumcols = bUseSelectedColumns ? selectedColumns.Count : srctable.DataColumns.ColumnCount; // check for the number of numeric columns int numcols = 0; for(int i=0;i<prenumcols;i++) { int idx = bUseSelectedColumns ? selectedColumns[i] : i; if(srctable[i] is Altaxo.Data.INumericColumn) numcols++; } // check the number of rows bool bUseSelectedRows = (null!=selectedRows && 0!=selectedRows.Count); int numrows; if(bUseSelectedRows) numrows = selectedRows.Count; else { numrows = 0; for(int i=0;i<numcols;i++) { int idx = bUseSelectedColumns ? selectedColumns[i] : i; numrows = Math.Max(numrows,srctable[idx].Count); } } // check that both dimensions are at least 2 - otherwise PCA is not possible if(numrows<2) return "At least two rows are neccessary to do Principal Component Analysis!"; if(numcols<2) return "At least two numeric columns are neccessary to do Principal Component Analysis!"; // Create a matrix of appropriate dimensions and fill it MatrixMath.BEMatrix matrixX; if(bHorizontalOrientedSpectrum) { matrixX = new MatrixMath.BEMatrix(numrows,numcols); int ccol = 0; // current column in the matrix for(int i=0;i<prenumcols;i++) { int colidx = bUseSelectedColumns ? selectedColumns[i] : i; Altaxo.Data.INumericColumn col = srctable[colidx] as Altaxo.Data.INumericColumn; if(null!=col) { for(int j=0;j<numrows;j++) { int rowidx = bUseSelectedRows ? selectedRows[j] : j; matrixX[j,ccol] = col[rowidx]; } ++ccol; } } } // end if it was a horizontal oriented spectrum else // if it is a vertical oriented spectrum { matrixX = new MatrixMath.BEMatrix(numcols,numrows); int ccol = 0; // current column in the matrix for(int i=0;i<prenumcols;i++) { int colidx = bUseSelectedColumns ? selectedColumns[i] : i; Altaxo.Data.INumericColumn col = srctable[colidx] as Altaxo.Data.INumericColumn; if(null!=col) { for(int j=0;j<numrows;j++) { int rowidx = bUseSelectedRows ? selectedRows[j] : j; matrixX[ccol,j] = col[rowidx]; } ++ccol; } } } // if it was a vertical oriented spectrum // now do PCA with the matrix MatrixMath.REMatrix factors = new MatrixMath.REMatrix(0,0); MatrixMath.BEMatrix loads = new MatrixMath.BEMatrix(0,0); MatrixMath.BEMatrix residualVariances = new MatrixMath.BEMatrix(0,0); MatrixMath.HorizontalVector meanX = new MatrixMath.HorizontalVector(matrixX.Columns); // first, center the matrix MatrixMath.ColumnsToZeroMean(matrixX,meanX); MatrixMath.NIPALS_HO(matrixX,maxNumberOfFactors,1E-9,factors,loads,residualVariances); // now we have to create a new table where to place the calculated factors and loads // we will do that in a vertical oriented manner, i.e. even if the loads are // here in horizontal vectors: in our table they are stored in (vertical) columns Altaxo.Data.DataTable table = new Altaxo.Data.DataTable("PCA of " + srctable.Name); // Fill the Table table.Suspend(); // first of all store the meanscore { double meanScore = MatrixMath.LengthOf(meanX); MatrixMath.NormalizeRows(meanX); Altaxo.Data.DoubleColumn col = new Altaxo.Data.DoubleColumn(); for(int i=0;i<factors.Rows;i++) col[i] = meanScore; table.DataColumns.Add(col,"MeanFactor",Altaxo.Data.ColumnKind.V,0); } // first store the factors for(int i=0;i<factors.Columns;i++) { Altaxo.Data.DoubleColumn col = new Altaxo.Data.DoubleColumn(); for(int j=0;j<factors.Rows;j++) col[j] = factors[j,i]; table.DataColumns.Add(col,"Factor"+i.ToString(),Altaxo.Data.ColumnKind.V,1); } // now store the mean of the matrix { Altaxo.Data.DoubleColumn col = new Altaxo.Data.DoubleColumn(); for(int j=0;j<meanX.Columns;j++) col[j] = meanX[0,j]; table.DataColumns.Add(col,"MeanLoad",Altaxo.Data.ColumnKind.V,2); } // now store the loads - careful - they are horizontal in the matrix for(int i=0;i<loads.Rows;i++) { Altaxo.Data.DoubleColumn col = new Altaxo.Data.DoubleColumn(); for(int j=0;j<loads.Columns;j++) col[j] = loads[i,j]; table.DataColumns.Add(col,"Load"+i.ToString(),Altaxo.Data.ColumnKind.V,3); } // now store the residual variances, they are vertical in the vector { Altaxo.Data.DoubleColumn col = new Altaxo.Data.DoubleColumn(); for(int i=0;i<residualVariances.Rows;i++) col[i] = residualVariances[i,0]; table.DataColumns.Add(col,"ResidualVariance",Altaxo.Data.ColumnKind.V,4); } table.Resume(); mainDocument.DataTableCollection.Add(table); // create a new worksheet without any columns Current.ProjectService.CreateNewWorksheet(table); return null; }