@@ -31,6 +31,53 @@ class MultispatiPCA:
3131 :math:`H=1/(2n)*X^t(W+W^t)X` where `X` is matrix of `n` observations :math:`\\ times`
3232 `d` features, and `W` is a matrix of the connectivity between observations.
3333
34+ Parameters
35+ ----------
36+ n_components : int or tuple[int, int], optional
37+ Number of components to keep.
38+ If None, will keep all components (only supported for non-sparse `X`).
39+ If an int, it will keep the top `n_components`.
40+ If a tuple, it will keep the top and bottom `n_components` respectively.
41+ connectivity : scipy.sparse.sparray or scipy.sparse.spmatrix
42+ Matrix of row-wise neighbor definitions i.e. c\ :sub:`ij` is the connectivity of
43+ i :math:`\\ to` j. The matrix does not have to be symmetric. It can be a
44+ binary adjacency matrix or a matrix of connectivities in which case
45+ c\ :sub:`ij` should be larger if i and j are close.
46+ A distance matrix should be transformed to connectivities by e.g.
47+ calculating :math:`1-d/d_{max}` beforehand.
48+
49+ Raises
50+ ------
51+ ValueError
52+ If connectivity is not a square matrix.
53+ ZeroDivisionError
54+ If one of the observations has no neighbors.
55+
56+ Attributes
57+ ----------
58+ components_ : numpy.ndarray
59+ The estimated components: Array of shape `(n_components, n_features)`.
60+
61+ variance_ : numpy.ndarray
62+ The estimated variance part of the eigenvalues. Array of shape `(n_components,)`.
63+
64+ moransI_ : numpy.ndarray
65+ The estimated Moran's I part of the eigenvalues. Array of shape `(n_components,)`.
66+
67+ eigenvalues_ : numpy.ndarray
68+ The eigenvalues corresponding to each of the selected components. Array of shape
69+ `(n_components,)`.
70+
71+ n_components_ : int
72+ The estimated number of components.
73+
74+ n_samples_ : int
75+ Number of samples in the training data.
76+
77+ n_features_in_ : int
78+ Number of features seen during :term:`fit`.
79+
80+
3481 References
3582 ----------
3683 `Dray, Stéphane, Sonia Saïd, and Françis Débias. "Spatial ordination of vegetation
@@ -47,29 +94,6 @@ def __init__(
4794 * ,
4895 connectivity : sparray | spmatrix ,
4996 ):
50- """
51- Parameters
52- ----------
53-
54- n_components : int or tuple[int, int], optional
55- Number of components to keep.
56- If None, will keep all components (only supported for non-sparse `X`).
57- If an int, it will keep the top `n_components`.
58- If a tuple, it will keep the top and bottom `n_components` respectively.
59- connectivity : scipy.sparse.sparray or scipy.sparse.spmatrix
60- Matrix of row-wise neighbor definitions i.e. c\ :sub:`ij` is the connectivity of
61- i :math:`\\ to` j. The matrix does not have to be symmetric. It can be a
62- binary adjacency matrix or a matrix of connectivities in which case
63- c\ :sub:`ij` should be larger if i and j are close.
64- A distance matrix should be transformed to connectivities by e.g.
65- calculating :math:`1-d/d_{max}` beforehand.
66- Raises
67- ------
68- ValueError
69- If connectivity is not a square matrix.
70- ZeroDivisionError
71- If one of the observations has no neighbors.
72- """
7397 self ._fitted = False
7498 W = csr_array (connectivity )
7599 if W .shape [0 ] != W .shape [1 ]:
@@ -247,7 +271,10 @@ def transform(self, X: _X) -> np.ndarray:
247271 self ._not_fitted ()
248272
249273 X = scale (X , with_mean = not issparse (X ))
250- return X @ self .components_
274+ X_t = X @ self .components_
275+ self .variance_ , self .moransI_ = self ._variance_moransI_decomposition (X_t )
276+
277+ return X_t
251278
252279 def fit_transform (self , X : _X ) -> np .ndarray :
253280 """
@@ -291,36 +318,17 @@ def transform_spatial_lag(self, X: _X) -> np.ndarray:
291318 def _spatial_lag (self , X : np .ndarray ) -> np .ndarray :
292319 return self .W @ X
293320
294- def variance_moransI_decomposition (self , X : _X ) -> tuple [np .ndarray , np .ndarray ]:
295- """
296- Calculate the decomposition of the variance and Moran's I for `n_components`.
297-
298- Parameters
299- ----------
300- X : numpy.ndarray or scipy.sparse.csr_array or scipy.sparse.csc_array
301- Array of observations x features.
302-
303- Returns
304- -------
305- tuple[numpy.ndarray, numpy.ndarray]
306- Variance and Moran's I.
307-
308- Raises
309- ------
310- ValueError
311- If instance has not been fitted.
312- """
313- if not self ._fitted :
314- self ._not_fitted ()
315- transformed = self .transform (X )
316- lag = self ._spatial_lag (transformed )
321+ def _variance_moransI_decomposition (
322+ self , X_t : np .ndarray
323+ ) -> tuple [np .ndarray , np .ndarray ]:
324+ lag = self ._spatial_lag (X_t )
317325
318326 # vector of row_Weights from dudi.PCA
319- # (we only use default row_weights i.e. 1/n anyways )
320- w = 1 / X .shape [0 ]
327+ # (we only use default row_weights i.e. 1/n)
328+ w = 1 / X_t .shape [0 ]
321329
322- variance = np .sum (transformed * transformed * w , axis = 0 )
323- moran = np .sum (transformed * lag * w , axis = 0 ) / variance
330+ variance = np .sum (X_t * X_t * w , axis = 0 )
331+ moran = np .sum (X_t * lag * w , axis = 0 ) / variance
324332
325333 return variance , moran
326334
0 commit comments