/// <summary> /// Perform CCA analysis on columns of two number tables. /// </summary> /// <param name="tableX">A number table.</param> /// <param name="tableY">A number table.</param> /// <returns>A PlsResult structure.</returns> /// <remarks>This method finds directions for tableX and tableY; so that these two tables will /// have maximal correlation in those directions.</remarks> public PlsResult DoCCA(INumberTable tableX, INumberTable tableY) { double[][] X = (double[][])tableX.Matrix; double[][] Y = (double[][])tableY.Matrix; double[][] Cxy = Covariance(X, Y); double[][] Cyx = Covariance(Y, X); IMathAdaptor math = MultivariateAnalysisPlugin.App.GetMathAdaptor(); double[][] rCxx = math.InvertMatrix(Covariance(X, X)); double[][] rCyy = math.InvertMatrix(Covariance(Y, Y)); MakeSymmetric(rCxx); MakeSymmetric(rCyy); double[][] A = MatrixProduct(rCxx, Cxy); double[][] B = MatrixProduct(rCyy, Cyx); double[][] Cx = MatrixProduct(A, B); double[][] Cy = MatrixProduct(B, A); double[][] Wx, Wy; double[] Rx, Ry; math.EigenDecomposition(Cx, out Wx, out Rx); math.EigenDecomposition(Cy, out Wy, out Ry); // // Rx and Ry should be the equal upto permutation and dimension. // Wy can be calculated from Wx by Ry = sqrt(1/Rx) * B * Rx // /* * double x0 = Math.Sqrt(Rx[0]); * double x1 = Math.Sqrt(Rx[1]); * double x2 = Math.Sqrt(Rx[2]); * * double y0 = Math.Sqrt(Ry[0]); * double y1 = Math.Sqrt(Ry[1]); * double y2 = Math.Sqrt(Ry[2]); * * 0.7165 0.4906 0.2668 */ SortEigenVectors(Wx, Rx); SortEigenVectors(Wy, Ry); //ValidateMatrix(Wx); //ValidateMatrix(Wy); PlsResult ret = new PlsResult(); ret.ProjectionX = EigenProjection(tableX, Wx); ret.ProjectionY = EigenProjection(tableY, Wy); ret.EigenVectorsX = Wx; ret.EigenVectorsY = Wy; ret.EigenValuesX = Rx; ret.EigenValuesY = Ry; return(ret); }
/// <summary> /// Perform PLS analysis on columns of two number tables. /// </summary> /// <param name="tableX">A number table.</param> /// <param name="tableY">A number table.</param> /// <returns>A PlsResult structure.</returns> /// <remarks>This method finds directions for tableX and tableY; so that these two tables will /// have maximal covariance in those directions.</remarks> public PlsResult DoPLS(INumberTable tableX, INumberTable tableY) { double[][] Cxy = Covariance((double[][])tableX.Matrix, (double[][])tableY.Matrix); double[][] Cyx = Covariance((double[][])tableY.Matrix, (double[][])tableX.Matrix); double[][] CxyCyx = MatrixProduct(Cxy, Cyx); double[][] CyxCxy = MatrixProduct(Cyx, Cxy); IMathAdaptor math = MultivariateAnalysisPlugin.App.GetMathAdaptor(); double[][] Wx, Wy; double[] Rx, Ry; // These two matrix might be slightly asymmetric due to calculation errors. MakeSymmetric(CxyCyx); MakeSymmetric(CyxCxy); math.EigenDecomposition(CxyCyx, out Wx, out Rx); math.EigenDecomposition(CyxCxy, out Wy, out Ry); SortEigenVectors(Wx, Rx); SortEigenVectors(Wy, Ry); PlsResult ret = new PlsResult(); ret.ProjectionX = EigenProjection(tableX, Wx); ret.ProjectionY = EigenProjection(tableY, Wy); ret.EigenVectorsX = Wx; ret.EigenVectorsY = Wy; ret.EigenValuesX = Rx; ret.EigenValuesY = Ry; return(ret); }
public void Initialize(IDataset dataset, XmlElement filterNode) { INumberTable numTable = dataset.GetNumberTable(); int N = numTable.Rows; double[][] L = Matrix(N, N); int offset = numTable.Columns - N; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (i != j) { L[i][j] = -numTable.Matrix[i][offset + j]; } else { L[i][j] = 0; } } } for (int i = 0; i < N; i++) { double rowSum = 0; for (int j = 0; j < N; j++) { rowSum += L[i][j]; } L[i][i] = -rowSum; } IMathAdaptor math = GraphMetrics.App.GetMathAdaptor(); double[][] H = math.InvertMatrix(L); d = Matrix(N, N); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { d[i][j] = H[i][i] + H[j][j] - H[i][j] - H[j][i]; } } }
/// <summary> /// Performs the Linear Discreminate Analysis (LDA) on a given number table. /// </summary> /// <param name="table">A numerical data table.</param> /// <returns>A LdaResult structure</returns> /// <remarks>This method find a set of directions, so that the projection of the row vector of the input data /// table to those directions will maximally separate rows with different types.</remarks> public LdaResult DoLDA(INumberTable table) { int rows = table.Rows; int columns = table.Columns; double[][] m = (double[][])table.Matrix; int classNumber = 0; // the number of classes Dictionary <int, int> type2ClassIndex = new Dictionary <int, int>(); int[] row2ClassIndex = new int[rows]; for (int row = 0; row < rows; row++) { int type = table.RowSpecList[row].Type; if (!type2ClassIndex.ContainsKey(type)) { type2ClassIndex[type] = classNumber++; } row2ClassIndex[row] = type2ClassIndex[type]; } // Calculate the class mean vectors. double[][] classMean = NewMatrix(classNumber, columns); for (int cIdx = 0; cIdx < classNumber; cIdx++) { Array.Clear(classMean[cIdx], 0, columns); } double[] totalMean = new double[columns]; Array.Clear(totalMean, 0, columns); int[] classSize = new int[classNumber]; Array.Clear(classSize, 0, classSize.Length); for (int row = 0; row < rows; row++) { int cIdx = row2ClassIndex[row]; classSize[cIdx]++; for (int col = 0; col < columns; col++) { double v = m[row][col]; totalMean[col] += v; classMean[cIdx][col] += v; } } for (int col = 0; col < columns; col++) { for (int cIdx = 0; cIdx < classNumber; cIdx++) { classMean[cIdx][col] /= classSize[cIdx]; } totalMean[col] /= rows; } // Zero mean the matrix m and the classMean vectors. double[][] z = NewMatrix(rows, columns); for (int row = 0; row < rows; row++) { int cIdx = row2ClassIndex[row]; for (int col = 0; col < columns; col++) { z[row][col] = m[row][col] - classMean[cIdx][col]; } } for (int cIdx = 0; cIdx < classNumber; cIdx++) { for (int col = 0; col < columns; col++) { classMean[cIdx][col] -= totalMean[col]; } } // Calculate the within- and between- covariance matrix. double[][] Sw = NewMatrix(columns, columns); double[][] Sb = NewMatrix(columns, columns); double[][] S = NewMatrix(columns, columns); for (int i = 0; i < columns; i++) { for (int j = 0; j < columns; j++) { double v = 0.0; for (int row = 0; row < rows; row++) { v += z[row][i] * z[row][j]; } Sw[i][j] = v; v = 0.0; for (int cIdx = 0; cIdx < classNumber; cIdx++) { v += classMean[cIdx][i] * classMean[cIdx][j]; } Sb[i][j] = v; } } // Calculate the inverse of Sw, and then Sb/Sw into S. IMathAdaptor math = MultivariateAnalysisPlugin.App.GetMathAdaptor(); Sw = math.InvertMatrix(Sw); if (Sw == null) { S = Sb; } else { for (int i = 0; i < columns; i++) { for (int j = 0; j < columns; j++) { double v = 0.0; for (int k = 0; k < columns; k++) { v += Sw[i][k] * Sb[k][j]; } S[i][j] = v; } } } // // REVISIT: Make the matrix S symmetric. This matrix should be symmetrical // by itself, but is normally not completely symmetric due to caculation errors (particularily, // the matrix Sw Inverted is mostly not symmetrically.) Notice: the method math.EigenDecomposition() // follows completely different algorithm when the matrix is not symmetric // MakeSymmetric(S); double[][] eigenVectors; double[] eigenValues; if (!math.EigenDecomposition(S, out eigenVectors, out eigenValues)) { MultivariateAnalysisPlugin.App.ScriptApp.LastError = "Failed to calculate the eigenvectors."; return(null); } /* * MultivariateAnalysisPlugin.App.ScriptApp.New.NumberTable(S).ShowAsTable(); * INumberTable eT = MultivariateAnalysisPlugin.App.ScriptApp.New.NumberTable(eigenVectors); * eT.AddRows(1); * for (int i = 0; i < eigenValues.Length; i++) eT.Matrix[eT.Rows - 1][i] = eigenValues[i]; * eT.RowSpecList[eT.Rows - 1].Id = "EV"; * eT.ShowAsTable(); */ SortEigenVectors(eigenVectors, eigenValues); // project the original data m to the eigenvectors. LdaResult ret = new LdaResult(); ret.EigenVectors = MultivariateAnalysisPlugin.App.ScriptApp.New.NumberTable(eigenVectors); int columnNumber = Math.Min(ret.EigenVectors.Columns, table.Columns); for (int col = 0; col < columnNumber; col++) { ret.EigenVectors.ColumnSpecList[col].CopyFrom(table.ColumnSpecList[col]); } ret.EigenValues = eigenValues; ret.Projection = EigenProjection(table, eigenVectors); return(ret); }