Changelog¶
All notable changes are recorded here. The format loosely follows Keep a Changelog.
Stability policy¶
While the version is 0.x the public API may change without a deprecation
cycle. From 1.0 onward, public-API changes will follow scikit-learn's
deprecation pattern (utils.deprecated, FutureWarning, a two-release window),
and default-value changes will be documented here.
Unreleased¶
0.1.0 — 2026-09-07¶
First public release: the OPLS regressor, the OPLSDA binary classifier and
the O2PLS two-block estimator, all scikit-learn compatible, with VIP scores,
permutation testing and diagnostic plots.
Items under Changed and Removed describe how the API settled during development. No earlier version was published, so nothing here breaks a released interface.
Added¶
OPLS, an OPLS regressor and supervised transformer: OSC-style orthogonal filtering followed byPLSRegressionon the cleanedX.OPLSDA, a binary classifier composingOPLSagainst a -1/+1 dummy response.O2PLS, a dense two-block estimator with X/Y preprocessing, sequential X- and Y-orthogonal filtering, final joint-subspace re-estimation, and bidirectionalpredict/predict_x. The v1 implementation is dense only and exposescoef_filtered_for scaled, X-filtered inputs rather than a raw-spacecoef_alias.OPLS.coef_raw_/OPLS.intercept_raw_: linear coefficients on the original raw input feature space, collapsing scaling, the orthogonal filter and the predictive PLS into one map, soX @ coef_raw_.T + intercept_raw_reproducespredict(X). No bare sklearncoef_alias is exposed (it would be the raw-space coefficient, not the engine's filtered-space one).OPLS.filter_transform(X)returns the preprocessed, orthogonal-filteredXactually passed to the predictive PLS engine (sopls_.predict(filter_transform(X))matchespredict(X)); useful for diagnostics and downstream modelling.OPLS.get_feature_names_outsoset_output(transform="pandas")yields named predictive-score columns (opls_pred0, …).- Lazy
vip_/ortho_vip_properties onOPLS(and onOPLSDA, delegating to the inner OPLS), following scikit-learn'sfeature_importances_convention — computed on access, not eagerly infit. Feature selection is supported viaSelectFromModel(OPLS(), importance_getter="vip_", threshold=1.0)(the VIP > 1 rule), composable in aPipeline/GridSearchCV. OPLSScoresDisplayandSPlotDisplayplotting classes following scikit-learn's Display convention (from_estimator(...),plot(ax=...),ax_/figure_).n_jobsonvalidation.permutation_test(runs the independent permutations in parallel; reproducible regardless ofn_jobs). Cross-validatedn_orthogonalselection inheritsn_jobsfromGridSearchCV._orthogonal.orthogonal_filter, a block-agnostic OSC-style deflation primitive shared byopls_filterandO2PLS.- Richer
__sklearn_tags__(target_tags.required,input_tags.sparse=False,non_deterministic=False) with tests asserting the resolved tags. ConvergenceWarningwhen the orthogonal filter truncates early.- Input validation (
check_array,check_consistent_length) and ann_permutationsguard inpermutation_test;check_arrayin the plotting helpers. - Full numpydoc docstrings on all public methods and functions.
- Zensical documentation site (
zensical.toml, mkdocstrings, numpy docstring style) with azensical buildCI gate, a GitHub Pages (Actions) deploy workflow, anddocs/citing.md. - Packaging metadata for PyPI: SPDX
licenseexpression with the bundledLICENSEfile,authors/maintainers,keywordsand troveclassifiers. .github/workflows/release.yml: tag-driven build, PyPI publication through Trusted Publishing (OIDC, no stored token), signed build attestations and an automatic GitHub release. It refuses to publish when the tag does not match__version__.- GitHub Actions CI (lint, format, type-check, tests, pre-commit) on Linux,
macOS and Windows across Python 3.12, 3.13 and 3.14, plus a
packagejob that builds the sdist and wheel, runstwine check --strict, and imports the package from each installed artifact in a clean environment. - Explicit Ruff rule selection (
E,W,F,I,N,UP,D, numpy docstring convention),pytest-covand[tool.coverage]configuration. CONTRIBUTING.md, a pull-request template, andRELEASING.md.
Changed¶
- Renamed the second parameter of
O2PLS.fitfromYtoy, matching the scikit-learn estimator contract (sklearn's owncross_decompositiondeprecatedYin favour ofy). Positional calls are unaffected; keyword calls must usefit(X, y=...). Y-block-specific helpers (predict_x,transform_y,filter_transform_y, …) keep their uppercaseYblock argument, as they are outside the sklearn contract. - Renamed the fitted attribute
rmsee_tormse_(uncorrected training root mean squared error). The old name implied a degrees-of-freedom-corrected calibration error, which it never computed; no alias is kept. predictive_weight(X, Y)now uses the leading left singular vector ofXᵀY, generalising to multivariateY. For single-columnYthe direction is unchanged (up to sign) and single-yOPLS output is bit-for-bit identical.- Cross-validated selection of
n_orthogonalis done with scikit-learn'sGridSearchCVdirectly — there is no bespoke selection API.OPLS.n_orthogonalis a plainint. UseGridSearchCV(OPLS(...), {"n_orthogonal": [...]}).fit(X, y)and readbest_params_["n_orthogonal"],best_estimator_andcv_results_["mean_test_score"]. For a parsimony bias, pass arefitcallable (recipe in the README / quickstart). ForOPLSDA, useGridSearchCV(OPLSDA(), {"n_orthogonal": [...]}, scoring="roc_auc"), which gives stratified folds for classification. matplotlibis an optional dependency in theplotextra (pip install "scikit-opls[plot]"). Onlyscikit_opls.plottingneeds it and it is imported lazily.OPLSDAuses its fittedLabelEncoderas the single class-label source.- The supported Python floor is 3.12 (
requires-python = ">=3.12"); lint and type checks target 3.12 while development happens on 3.13 (.python-version). - Numerical tests use
sklearn.utils._testing.assert_allclose. - Pinned the pre-commit
ruffrev to the dev-groupruffversion.
Removed¶
O2PLS.score; it duplicated the inheritedRegressorMixin.score(R² ofpredict(X)againsty) with identical behaviour.scoreremains available via the mixin.OPLSDA'sprobabilityparameter and its in-sample Platt calibration (predict_proba,raw_score).OPLSDAis now a clean score classifier:decision_functionreturns the raw signed OPLS regression output andpredictits sign. For probabilities, wrap inCalibratedClassifierCV(OPLSDA(...))(cross-fitted, better calibrated). This also removes thepredict/predict_probaboundary inconsistency the in-sample calibrator caused.- The
"auto"option and thecvparameter onOPLSandOPLSDA, theOPLSCVestimator, and theselection.select_orthogonalfactory. Use theGridSearchCVrecipe under Changed.OPLSDACVwill not be added. - The public
scikit_opls.inspectionmodule and itsvip(model)/orthogonal_vip(model)functions; the stateless math moved to a private_inspectionmodule and is reached through thevip_properties.
Fixed¶
- Orthogonal filtering no longer extracts components past the point where a
block's rank is exhausted. Both
OPLS's filter and O2PLS's block-specific extraction judged convergence against the current deflated block, whose sum of squares shrinks with every deflation, so rounding noise stayed significant relative to itself. Convergence is now measured against the original block and the component count is bounded bymin(n_samples, n_features). Fittedn_orthogonal_,n_x_orthogonal_andn_y_orthogonal_on rank-deficient data may be lower than before, and no longer vary with the BLAS implementation. - O2PLS orthogonal extraction is now invariant to a global rescaling of the
blocks. Resolvability was measured against
max(block_ssq, 1.0), an absolute floor in the units of the data, so identical blocks yielded different component counts depending only on their scale.
Documentation¶
- Completed the numpydoc
Attributessections ofOPLS,OPLSDAandO2PLS(per-component diagnostics, training Q residuals,b_t_/b_u_,n_features_in_/feature_names_in_, etc.), documented thatO2PLS.x_filtered_/y_filtered_/x_residuals_/y_residuals_make the fitted estimator scale with training-data size, and noted that the stringscaleparameter differs fromPLSRegression's booleanscale. OPLS.scoredocstring documenting the inheritedRegressorMixinR² score.CITATION.cffgainedtype, split author names, ORCID,version,license,repository-code, keywords and the method references.RELEASING.mddescribes the automated tag-driven release and namessrc/scikit_opls/version.pyas the single source of truth for the version.