autoPCA TableOfReal_to_PCA (I) { iam (TableOfReal); try { long m = my numberOfRows, n = my numberOfColumns; if (! TableOfReal_areAllCellsDefined (me, 0, 0, 0, 0)) { Melder_throw (U"Undefined cells."); } if (m < 2) { Melder_throw (U"There is not enough data to perform a PCA.\nYour table has less than 2 rows."); } if (m < n) { Melder_warning (U"The number of rows in your table is less than the \nnumber of columns. "); } if (NUMfrobeniusnorm (m, n, my data) == 0) { Melder_throw (U"All values in your table are zero."); } autoPCA thee = Thing_new (PCA); autoNUMmatrix<double> a (NUMmatrix_copy (my data, 1, m, 1, n), 1, 1); thy centroid = NUMvector<double> (1, n); for (long j = 1; j <= n; j++) { double colmean = a[1][j]; for (long i = 2; i <= m; i++) { colmean += a[i][j]; } colmean /= m; for (long i = 1; i <= m; i++) { a[i][j] -= colmean; } thy centroid[j] = colmean; } Eigen_initFromSquareRoot (thee.peek(), a.peek(), m, n); thy labels = NUMvector<char32 *> (1, n); NUMstrings_copyElements (my columnLabels, thy labels, 1, n); PCA_setNumberOfObservations (thee.peek(), m); /* The covariance matrix C = A'A / (N-1). However, we have calculated the eigenstructure for A'A. This has no consequences for the eigenvectors, but the eigenvalues have to be divided by (N-1). */ for (long i = 1; i <= thy numberOfEigenvalues; i++) { thy eigenvalues[i] /= (m - 1); } return thee; } catch (MelderError) { Melder_throw (me, U": PCA not created."); } }
static autoPCA NUMdmatrix_to_PCA (double **m, long numberOfRows, long numberOfColumns, bool byColumns) { try { if (! NUMdmatrix_hasFiniteElements(m, 1, numberOfRows, 1, numberOfColumns)) { Melder_throw (U"At least one of the matrix elements is not finite or undefined."); } if (NUMfrobeniusnorm (numberOfRows, numberOfColumns, m) == 0.0) { Melder_throw (U"All values in your table are zero."); } autoNUMmatrix<double> mcopy; long numberOfRows2, numberOfColumns2; if (byColumns) { if (numberOfColumns < numberOfRows) { Melder_warning (U"The number of columns in your table is less than the number of rows. "); } numberOfRows2 = numberOfColumns, numberOfColumns2 = numberOfRows; mcopy.reset (1, numberOfRows2, 1, numberOfColumns2); for (long i = 1; i <= numberOfRows2; i ++) { // transpose for (long j = 1; j <= numberOfColumns2; j++) { mcopy [i] [j] = m [j] [i]; } } } else { if (numberOfRows < numberOfColumns) { Melder_warning (U"The number of rows in your table is less than the number of columns. "); } numberOfRows2 = numberOfRows, numberOfColumns2 = numberOfColumns; mcopy.reset (1, numberOfRows2, 1, numberOfColumns2); NUMmatrix_copyElements<double>(m, mcopy.peek(), 1, numberOfRows2, 1, numberOfColumns2); } autoPCA thee = Thing_new (PCA); thy centroid = NUMvector<double> (1, numberOfColumns2); NUMcentreColumns (mcopy.peek(), 1, numberOfRows2, 1, numberOfColumns2, thy centroid); Eigen_initFromSquareRoot (thee.get(), mcopy.peek(), numberOfRows2, numberOfColumns2); thy labels = NUMvector<char32 *> (1, numberOfColumns2); PCA_setNumberOfObservations (thee.get(), numberOfRows2); /* The covariance matrix C = A'A / (N-1). However, we have calculated the eigenstructure for A'A. This has no consequences for the eigenvectors, but the eigenvalues have to be divided by (N-1). */ for (long i = 1; i <= thy numberOfEigenvalues; i++) { thy eigenvalues [i] /= (numberOfRows2 - 1); } return thee; } catch (MelderError) { Melder_throw (U"No PCA created from ", byColumns ? U"columns." : U"rows."); } }