op_princomp_meat.hpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. // Copyright 2008-2016 Conrad Sanderson (http://conradsanderson.id.au)
  2. // Copyright 2008-2016 National ICT Australia (NICTA)
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // ------------------------------------------------------------------------
  15. //! \addtogroup op_princomp
  16. //! @{
  17. //! \brief
  18. //! principal component analysis -- 4 arguments version
  19. //! computation is done via singular value decomposition
  20. //! coeff_out -> principal component coefficients
  21. //! score_out -> projected samples
  22. //! latent_out -> eigenvalues of principal vectors
  23. //! tsquared_out -> Hotelling's T^2 statistic
  24. template<typename T1>
  25. inline
  26. bool
  27. op_princomp::direct_princomp
  28. (
  29. Mat<typename T1::elem_type>& coeff_out,
  30. Mat<typename T1::elem_type>& score_out,
  31. Col<typename T1::pod_type>& latent_out,
  32. Col<typename T1::elem_type>& tsquared_out,
  33. const Base<typename T1::elem_type, T1>& X
  34. )
  35. {
  36. arma_extra_debug_sigprint();
  37. typedef typename T1::elem_type eT;
  38. typedef typename T1::pod_type T;
  39. const unwrap_check<T1> Y( X.get_ref(), score_out );
  40. const Mat<eT>& in = Y.M;
  41. const uword n_rows = in.n_rows;
  42. const uword n_cols = in.n_cols;
  43. if(n_rows > 1) // more than one sample
  44. {
  45. // subtract the mean - use score_out as temporary matrix
  46. score_out = in; score_out.each_row() -= mean(in);
  47. // singular value decomposition
  48. Mat<eT> U;
  49. Col< T> s;
  50. const bool svd_ok = (n_rows >= n_cols) ? svd_econ(U, s, coeff_out, score_out) : svd(U, s, coeff_out, score_out);
  51. if(svd_ok == false) { return false; }
  52. // normalize the eigenvalues
  53. s /= std::sqrt( double(n_rows - 1) );
  54. // project the samples to the principals
  55. score_out *= coeff_out;
  56. if(n_rows <= n_cols) // number of samples is less than their dimensionality
  57. {
  58. score_out.cols(n_rows-1,n_cols-1).zeros();
  59. Col<T> s_tmp(n_cols, fill::zeros);
  60. s_tmp.rows(0,n_rows-2) = s.rows(0,n_rows-2);
  61. s = s_tmp;
  62. // compute the Hotelling's T-squared
  63. s_tmp.rows(0,n_rows-2) = T(1) / s_tmp.rows(0,n_rows-2);
  64. const Mat<eT> S = score_out * diagmat(Col<T>(s_tmp));
  65. tsquared_out = sum(S%S,1);
  66. }
  67. else
  68. {
  69. // compute the Hotelling's T-squared
  70. // TODO: replace with more robust approach
  71. const Mat<eT> S = score_out * diagmat(Col<T>( T(1) / s));
  72. tsquared_out = sum(S%S,1);
  73. }
  74. // compute the eigenvalues of the principal vectors
  75. latent_out = s%s;
  76. }
  77. else // 0 or 1 samples
  78. {
  79. coeff_out.eye(n_cols, n_cols);
  80. score_out.copy_size(in);
  81. score_out.zeros();
  82. latent_out.set_size(n_cols);
  83. latent_out.zeros();
  84. tsquared_out.set_size(n_rows);
  85. tsquared_out.zeros();
  86. }
  87. return true;
  88. }
  89. //! \brief
  90. //! principal component analysis -- 3 arguments version
  91. //! computation is done via singular value decomposition
  92. //! coeff_out -> principal component coefficients
  93. //! score_out -> projected samples
  94. //! latent_out -> eigenvalues of principal vectors
  95. template<typename T1>
  96. inline
  97. bool
  98. op_princomp::direct_princomp
  99. (
  100. Mat<typename T1::elem_type>& coeff_out,
  101. Mat<typename T1::elem_type>& score_out,
  102. Col<typename T1::pod_type>& latent_out,
  103. const Base<typename T1::elem_type, T1>& X
  104. )
  105. {
  106. arma_extra_debug_sigprint();
  107. typedef typename T1::elem_type eT;
  108. typedef typename T1::pod_type T;
  109. const unwrap_check<T1> Y( X.get_ref(), score_out );
  110. const Mat<eT>& in = Y.M;
  111. const uword n_rows = in.n_rows;
  112. const uword n_cols = in.n_cols;
  113. if(n_rows > 1) // more than one sample
  114. {
  115. // subtract the mean - use score_out as temporary matrix
  116. score_out = in; score_out.each_row() -= mean(in);
  117. // singular value decomposition
  118. Mat<eT> U;
  119. Col< T> s;
  120. const bool svd_ok = (n_rows >= n_cols) ? svd_econ(U, s, coeff_out, score_out) : svd(U, s, coeff_out, score_out);
  121. if(svd_ok == false) { return false; }
  122. // normalize the eigenvalues
  123. s /= std::sqrt( double(n_rows - 1) );
  124. // project the samples to the principals
  125. score_out *= coeff_out;
  126. if(n_rows <= n_cols) // number of samples is less than their dimensionality
  127. {
  128. score_out.cols(n_rows-1,n_cols-1).zeros();
  129. Col<T> s_tmp(n_cols, fill::zeros);
  130. s_tmp.rows(0,n_rows-2) = s.rows(0,n_rows-2);
  131. s = s_tmp;
  132. }
  133. // compute the eigenvalues of the principal vectors
  134. latent_out = s%s;
  135. }
  136. else // 0 or 1 samples
  137. {
  138. coeff_out.eye(n_cols, n_cols);
  139. score_out.copy_size(in);
  140. score_out.zeros();
  141. latent_out.set_size(n_cols);
  142. latent_out.zeros();
  143. }
  144. return true;
  145. }
  146. //! \brief
  147. //! principal component analysis -- 2 arguments version
  148. //! computation is done via singular value decomposition
  149. //! coeff_out -> principal component coefficients
  150. //! score_out -> projected samples
  151. template<typename T1>
  152. inline
  153. bool
  154. op_princomp::direct_princomp
  155. (
  156. Mat<typename T1::elem_type>& coeff_out,
  157. Mat<typename T1::elem_type>& score_out,
  158. const Base<typename T1::elem_type, T1>& X
  159. )
  160. {
  161. arma_extra_debug_sigprint();
  162. typedef typename T1::elem_type eT;
  163. typedef typename T1::pod_type T;
  164. const unwrap_check<T1> Y( X.get_ref(), score_out );
  165. const Mat<eT>& in = Y.M;
  166. const uword n_rows = in.n_rows;
  167. const uword n_cols = in.n_cols;
  168. if(n_rows > 1) // more than one sample
  169. {
  170. // subtract the mean - use score_out as temporary matrix
  171. score_out = in; score_out.each_row() -= mean(in);
  172. // singular value decomposition
  173. Mat<eT> U;
  174. Col< T> s;
  175. const bool svd_ok = (n_rows >= n_cols) ? svd_econ(U, s, coeff_out, score_out) : svd(U, s, coeff_out, score_out);
  176. if(svd_ok == false) { return false; }
  177. // project the samples to the principals
  178. score_out *= coeff_out;
  179. if(n_rows <= n_cols) // number of samples is less than their dimensionality
  180. {
  181. score_out.cols(n_rows-1,n_cols-1).zeros();
  182. }
  183. }
  184. else // 0 or 1 samples
  185. {
  186. coeff_out.eye(n_cols, n_cols);
  187. score_out.copy_size(in);
  188. score_out.zeros();
  189. }
  190. return true;
  191. }
  192. //! \brief
  193. //! principal component analysis -- 1 argument version
  194. //! computation is done via singular value decomposition
  195. //! coeff_out -> principal component coefficients
  196. template<typename T1>
  197. inline
  198. bool
  199. op_princomp::direct_princomp
  200. (
  201. Mat<typename T1::elem_type>& coeff_out,
  202. const Base<typename T1::elem_type, T1>& X
  203. )
  204. {
  205. arma_extra_debug_sigprint();
  206. typedef typename T1::elem_type eT;
  207. typedef typename T1::pod_type T;
  208. const unwrap<T1> Y( X.get_ref() );
  209. const Mat<eT>& in = Y.M;
  210. if(in.n_elem != 0)
  211. {
  212. Mat<eT> tmp = in; tmp.each_row() -= mean(in);
  213. // singular value decomposition
  214. Mat<eT> U;
  215. Col< T> s;
  216. const bool svd_ok = (in.n_rows >= in.n_cols) ? svd_econ(U, s, coeff_out, tmp) : svd(U, s, coeff_out, tmp);
  217. if(svd_ok == false) { return false; }
  218. }
  219. else
  220. {
  221. coeff_out.eye(in.n_cols, in.n_cols);
  222. }
  223. return true;
  224. }
  225. template<typename T1>
  226. inline
  227. void
  228. op_princomp::apply
  229. (
  230. Mat<typename T1::elem_type>& out,
  231. const Op<T1,op_princomp>& in
  232. )
  233. {
  234. arma_extra_debug_sigprint();
  235. typedef typename T1::elem_type eT;
  236. const unwrap_check<T1> tmp(in.m, out);
  237. const Mat<eT>& A = tmp.M;
  238. const bool status = op_princomp::direct_princomp(out, A);
  239. if(status == false)
  240. {
  241. out.soft_reset();
  242. arma_stop_runtime_error("princomp(): decomposition failed");
  243. }
  244. }
  245. //! @}