diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ccf17..ef70a41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ those changes. ## [Unreleased] +- Docstring corrections across PCA/PLS/TPLS/limits/preprocessing/experiments + modules to bring documented behaviour into line with the code (no runtime + changes). + ## [1.54.0] - 2026-07-13 ### Changed diff --git a/CITATION.cff b/CITATION.cff index 42e08d4..ef6f9a0 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -12,8 +12,8 @@ authors: repository-code: "https://github.com/kgdunn/process-improve" url: "https://kgdunn.github.io/process-improve/" license: MIT -version: 1.54.0 -date-released: "2026-07-13" +version: 1.54.1 +date-released: "2026-07-17" keywords: - chemometrics - multivariate analysis diff --git a/pyproject.toml b/pyproject.toml index cc77242..74fae2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "process-improve" -version = "1.54.0" +version = "1.54.1" description = 'Designed Experiments; Latent Variables (PCA, PLS, multivariate methods with missing data); Process Monitoring; Batch data analysis.' readme = "README.md" license = "MIT" diff --git a/src/process_improve/experiments/designs_response_surface.py b/src/process_improve/experiments/designs_response_surface.py index cc0e58b..1b34f32 100644 --- a/src/process_improve/experiments/designs_response_surface.py +++ b/src/process_improve/experiments/designs_response_surface.py @@ -46,7 +46,9 @@ def dispatch_ccd( # noqa: PLR0913 alpha : str, float, or None Axial distance. Accepted string values: ``"rotatable"``, ``"face_centered"``, ``"orthogonal"``. A numeric value sets - alpha directly. Defaults to ``"orthogonal"``. + alpha directly **only when ``cube='fractional'``**; for + ``cube='full'`` the numeric value is ignored and the orthogonal + alpha is used. Defaults to ``"orthogonal"``. cube : str How to build the cube (factorial) portion: ``"full"`` (default) uses the complete 2^k factorial; ``"fractional"`` uses a resolution-V (or diff --git a/src/process_improve/multivariate/_limits.py b/src/process_improve/multivariate/_limits.py index da6094c..8e18f3f 100644 --- a/src/process_improve/multivariate/_limits.py +++ b/src/process_improve/multivariate/_limits.py @@ -89,8 +89,11 @@ def spe_calculation(spe_values: np.ndarray, conf_level: float = 0.95) -> float: Returns ------- float - The limit, above which we judge observations in the model to have a different correlation - structure than those values which were used to build the model. + The SPE limit at the requested confidence level, returned on the + sqrt-of-sum-of-squared-residuals scale (directly comparable to + entries of ``model.spe_``, which are stored on the same scale). + Values above this limit indicate observations whose correlation + structure differs from the training data's. """ if not 0.0 < conf_level < 1.0: raise ValueError(f"conf_level must lie in (0, 1); got {conf_level}.") @@ -181,8 +184,11 @@ def ellipse_coordinates( # noqa: PLR0913 Number of components `A` in the fitted model. Required to look up the Hotelling's T^2 limit and to bound `score_horiz`/`score_vert`. scaling_factor_for_scores : pd.Series - Per-component standard deviations of the scores (``model.scaling_factor_for_scores_``). - Used to scale the ellipse axes. + Per-component standard deviations of the scores + (``model.scaling_factor_for_scores_``). Used to scale the ellipse + axes. Required: the signature defaults to ``None`` for backward + compatibility, but passing ``None`` (or omitting the argument) + raises :class:`AssertionError` inside the function. n_rows : int Number of rows `N` in the data used to fit the model. Required to compute the Hotelling's T^2 limit; must be strictly positive. diff --git a/src/process_improve/multivariate/_pca.py b/src/process_improve/multivariate/_pca.py index 69189e4..3289d6a 100644 --- a/src/process_improve/multivariate/_pca.py +++ b/src/process_improve/multivariate/_pca.py @@ -101,8 +101,9 @@ def _pca_ekf_press( # noqa: PLR0913, PLR0915, PLR0912, C901 Returns ------- press : np.ndarray of shape (max_components,) - Per-cell PRESS per component count, averaged over ``n_repeats`` - passes so the scale is comparable to a single-pass run. + Total PRESS per component count (sum of squared cell-residuals + across all folds, averaged over ``n_repeats`` passes so the scale + is comparable to a single-pass run). per_fold_press : np.ndarray of shape (max_components, n_folds * n_repeats) Per-fold PRESS contributions across every fold of every repeat; drives the 1-SE rule's standard error. @@ -220,8 +221,11 @@ class PCA(_LatentVariableModel, TransformerMixin, BaseEstimator): Parameters ---------- - n_components : int - Number of principal components to extract. + n_components : int or None, default=None + Number of principal components to extract. If ``None``, defaults to + ``min(n_samples, n_features)`` when :meth:`fit` runs; if the requested + value exceeds that maximum, it is clamped to ``min(n_samples, + n_features)`` and a :class:`SpecificationWarning` is issued. algorithm : str, default="auto" Algorithm to use for fitting the model. @@ -920,8 +924,10 @@ def parallel_analysis( the more conservative 95th-percentile threshold is the modern recommendation. scale : bool, default True - Mean-centre and unit-variance scale ``X`` before estimation - (matches :meth:`minka_mle`). + Mean-centre and unit-variance scale ``X`` before estimation. + Note that :meth:`minka_mle`, by contrast, only centres ``X`` + (no unit-variance scaling); the two routines do not share the + same preprocessing when ``scale=True`` here. random_state : int, optional Seed for the null-matrix simulations. @@ -1069,6 +1075,15 @@ def select_n_components( # noqa: PLR0913, PLR0915, C901 step. Ignored under ``cv_scheme="row_wise"``. random_state : int, optional Seed for the ekf element-fold permutation. + return_consensus : bool, default False + If ``True``, also run :meth:`minka_mle` and + :meth:`parallel_analysis` on ``X`` and add their recommendations + plus a consensus vote to the returned :class:`~sklearn.utils.Bunch` + (extra keys: ``minka_n_components``, + ``parallel_analysis_n_components``, ``consensus``, + ``consensus_counts``). The primary ``n_components`` value is still + the cross-validation recommendation; the consensus fields are + informational. threshold : float, optional Deprecated. The original Wold PRESS-ratio cutoff. Passing it emits a :class:`DeprecationWarning`; the value is ignored. Use diff --git a/src/process_improve/multivariate/_pls.py b/src/process_improve/multivariate/_pls.py index ac14d85..e328f9d 100644 --- a/src/process_improve/multivariate/_pls.py +++ b/src/process_improve/multivariate/_pls.py @@ -149,8 +149,11 @@ class PLS(_LatentVariableModel, RegressorMixin, TransformerMixin, BaseEstimator) Parameters ---------- - n_components : int - Number of latent components to extract. + n_components : int or None, default=None + Number of latent components to extract. If ``None``, defaults to + ``min(n_samples, n_features)`` when :meth:`fit` runs; if the requested + value exceeds that maximum, it is clamped and a + :class:`SpecificationWarning` is issued. scale : bool, default=True Mean-center and unit-variance-scale both the X and Y blocks internally before fitting (``ddof=1``, done with :class:`MCUVScaler`). This mirrors diff --git a/src/process_improve/multivariate/_preprocessing.py b/src/process_improve/multivariate/_preprocessing.py index 14f54a7..c43fc9c 100644 --- a/src/process_improve/multivariate/_preprocessing.py +++ b/src/process_improve/multivariate/_preprocessing.py @@ -158,9 +158,10 @@ def center( This specifies the axis along which the centering vector will be calculated if not provided. The function is applied along the `axis`: 0=down the columns; 1 = across the rows. - *Missing values*: The sample mean is computed by taking the sum along the `axis`, skipping - any missing data, and dividing by N = number of values which are present. Values which were - missing before, are left as missing after. + *Missing values*: The default ``func=np.mean`` does not skip NaN reliably; a column + containing any NaN propagates NaN into the returned centring vector. Pass + ``func=np.nanmean`` (or a custom NaN-aware reducer) for explicit NaN-aware centring. + Values which were missing before are left as missing after. """ # pandas-stubs types apply()'s axis as a Literal, so a plain ``int`` axis does # not match any overload; the call is valid at runtime. @@ -185,9 +186,9 @@ def scale( `func` [optional; default=np.std] {a function} - The default (np.std) uses NumPy to calculate the standard deviation of - the data along the required `axis`, skipping over any missing data, and - uses that as `scale`. + The default (``np.std``) does not skip NaN reliably; a column containing any NaN + propagates NaN into the returned scaling vector. Pass ``func=np.nanstd`` (or a + custom NaN-aware reducer) for explicit NaN-aware scaling. `axis` [optional; default=0] {integer} Transformations are applied on slices of data. This specifies the diff --git a/src/process_improve/multivariate/_tpls.py b/src/process_improve/multivariate/_tpls.py index 62aa6b9..6deca69 100644 --- a/src/process_improve/multivariate/_tpls.py +++ b/src/process_improve/multivariate/_tpls.py @@ -352,8 +352,13 @@ def fit(self, X: DataFrameDict, y: None = None) -> TPLS: # noqa: ARG002, PLR091 Parameters ---------- - X : {dictionary of dataframes}, keys that must be present: "F", "Z", and "Y" - The training input samples. See documentation in the class definition for more information on each matrix. + X : DataFrameDict + The training input samples, provided as a + :class:`DataFrameDict` (a plain :class:`dict` is not accepted; + the method raises :class:`TypeError`). Keys that must be + present: ``"F"``, ``"Z"``, and ``"Y"``. See the class-level + :class:`TPLS` docstring (and its example) for the required + shape of each block. Returns -------