Metadata-Version: 2.4
Name: mot2d2pca
Version: 0.2.0
Summary: Mask-aware, temporally-penalized bilateral two-dimensional PCA (MOT-(2D)^2 PCA).
Project-URL: Homepage, https://github.com/GengS2000/mot2d2pca
Project-URL: Repository, https://github.com/GengS2000/mot2d2pca
Project-URL: Issues, https://github.com/GengS2000/mot2d2pca/issues
Author-email: GengS2000 <gengsmed@gmail.com>
License: MIT
License-File: LICENSE
Keywords: 2DPCA,PCA,dimensionality-reduction,longitudinal,microbiome,missing-data,tensor
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.9
Requires-Dist: numpy>=1.21
Provides-Extra: all
Requires-Dist: pandas>=1.3; extra == 'all'
Requires-Dist: scipy>=1.7; extra == 'all'
Requires-Dist: xarray>=2022.3.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: pandas>=1.3; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Requires-Dist: scipy>=1.7; extra == 'dev'
Requires-Dist: xarray>=2022.3.0; extra == 'dev'
Provides-Extra: eval
Requires-Dist: pandas>=1.3; extra == 'eval'
Requires-Dist: scipy>=1.7; extra == 'eval'
Provides-Extra: xarray
Requires-Dist: xarray>=2022.3.0; extra == 'xarray'
Description-Content-Type: text/markdown

# mot2d2pca

MOT-(2D)^2 PCA is a mask-aware, temporally penalized bilateral
two-dimensional PCA method for longitudinal tensors with shape `(T, m, n)`.
It uses the research definition

```text
A_eff[t] = where(observed[t], X[t], Abar)
C[t]     = Z.T @ A_eff[t] @ V
A_hat[t] = Abar + Z @ C[t] @ V.T
```

where `Abar` is the mask-aware per-entry temporal mean, `Z` and `V` are shared
row and column loadings, and `C[t]` is the time-specific score matrix.

## Missing-value and floor handling

The default pseudocount is `eps=1e-3`, so the default log10 floor is `-3`.
Finite values at or below `log10(eps)`, together with non-finite values, are
automatically excluded from fitting and designated for imputation.

An additional replacement mask can be supplied through `impute_mask`. Its
meaning is:

```text
False = keep the entry unless it is an automatic floor/non-finite target
True  = impute the entry
```

The explicit mask is combined with automatic floor detection. The legacy
`mask` parameter remains available as an observed-entry mask (`True` means
observed) for compatibility with the research scripts.

Use `eps=1e-2` when the input tensor was preprocessed with a `-2` floor, such
as the result-generating differential-abundance Simulation 3 workflow.

## Install

```bash
pip install mot2d2pca
pip install "mot2d2pca[eval]"
pip install "mot2d2pca[xarray]"
pip install "mot2d2pca[all]"
```

## Functional API

```python
import numpy as np
from mot2d2pca import fit_mot2d2pca

rng = np.random.default_rng(0)
X = rng.normal(-1.2, 0.4, size=(6, 40, 30))
X[rng.random(X.shape) < 0.1] = -3.0

result = fit_mot2d2pca(
    X,
    time=[0, 2, 4, 8, 16, 30],
    d=2,
    q=2,
    lam=0.1,
    sigma=4.0,
    eps=1e-3,
)

scores = result.scores
low_rank = result.reconstruct()
full_reconstruction = result.reconstruct_full()
X_imputed = result.impute(X)
```

`result.reconstruct()` intentionally returns only `Z @ C[t] @ V.T`, preserving
the version 0.1.0 and research-script contract. `result.reconstruct_full()`
adds `Abar` exactly once. `result.impute(X)` writes the full reconstruction only
to floor, non-finite, observed-mask-excluded, or explicitly masked entries and
preserves all remaining input values.

To request imputation of additional entries:

```python
impute_mask = np.zeros_like(X, dtype=bool)
impute_mask[:, 3, 7] = True

result = fit_mot2d2pca(X, impute_mask=impute_mask)
X_imputed = result.impute(X)
```

## Estimator API

```python
from mot2d2pca import MOT2D2PCA

model = MOT2D2PCA(d=2, q=2, lam=0.1, sigma=4.0, eps=1e-3)
model.fit(X, impute_mask=impute_mask)

scores = model.scores_
full_reconstruction = model.reconstruct_full()
X_imputed = model.impute(X)
```

## Labelled arrays

```python
from mot2d2pca.xarray_api import fit_mot2d2pca_xr

out = fit_mot2d2pca_xr(
    da,
    row_dim="participant",
    col_dim="taxon",
    time_dim="bin",
    d=2,
    q=2,
    eps=1e-3,
)

out["reconstruction"]
out["imputed"]
```

## Main parameters

| Name | Meaning |
|---|---|
| `d`, `q` | Number of column and row components |
| `lam` | Temporal-penalty strength |
| `sigma` | Gaussian time-neighborhood bandwidth |
| `T0`, `T` | Unpenalized and penalized iteration counts |
| `eps` | Pseudocount defining the automatic `log10(eps)` floor |
| `impute_mask` | Optional replacement mask; `True` means impute |
| `random_state` | Random initialization seed |

## Citation

If you use this package, cite the accompanying paper and the software metadata
in [`CITATION.cff`](CITATION.cff).

## License

MIT
