Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ exclude_lines =
raise NotImplementedError
# Don't track main entry points
if __name__ == .__main__.:
# Don't complain about TYPE_CHECKING-only imports
# Don't complain about TYPE_CHECKING-only imports (both the bare
# ``if TYPE_CHECKING:`` and the ``if typing.TYPE_CHECKING:`` form).
if TYPE_CHECKING:
if typing.TYPE_CHECKING:
# ENG-13 (#295): the fallback branches for each gated optional import
# cannot fire under CI (which installs all extras). Exclude them by
# matching lines that the fallback bodies use exclusively.
Expand Down
29 changes: 28 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,32 @@ those changes.

## [Unreleased]

## [1.59.0] - 2026-07-23

### Added

- `OPLS`: an Orthogonal Projections to Latent Structures estimator for a single
response (Trygg-Wold NIPALS), extracting one Y-predictive component and
`n_orthogonal_components` Y-orthogonal components. It exposes `fit`,
`transform` (predictive scores), `correct` (the orthogonal-signal-corrected
X), `predict`, and `invert`. `OPLS.invert()` finds the inputs for a desired
response through a single division and returns the orthogonal-space basis;
for a single response this is the same set of designs as `PLS.invert()`'s null
space (García-Carrión et al., 2025). An A-component PLS model and an
O-PLS(1; A - 1) model give identical predictions and regression coefficients.
- `PLS.invert()`: PLS model inversion for latent-variable product and process
design. Given a desired response on the original Y scale, it returns the
minimum-norm (direct-inversion) input vector that the model predicts will
achieve it, together with an orthonormal basis for the *null space* - the
`(A - r)`-dimensional family of input vectors that all yield the same
prediction (`A` components, response rank `r`). Callers can pass
`null_space_coordinates` to move along that space and satisfy secondary
criteria (cost, safety, operability) without changing the predicted response.
The result also reports the solution's Hotelling's T2 to flag extrapolation
beyond the calibration data. Reproduces the null-space results of
García-Carrión et al. (2025), whose single-response proof identifies this null
space with the orthogonal space of an O-PLS model.

## [1.58.0] - 2026-07-23

### Added
Expand Down Expand Up @@ -2599,7 +2625,8 @@ this entry records them together.
- Reworked the README with a sharper value proposition and a
"Why not scikit-learn?" comparison table.

[Unreleased]: https://github.com/kgdunn/process-improve/compare/v1.58.0...HEAD
[Unreleased]: https://github.com/kgdunn/process-improve/compare/v1.59.0...HEAD
[1.59.0]: https://github.com/kgdunn/process-improve/compare/v1.58.0...v1.59.0
[1.58.0]: https://github.com/kgdunn/process-improve/compare/v1.57.0...v1.58.0
[1.57.0]: https://github.com/kgdunn/process-improve/compare/v1.56.0...v1.57.0
[1.56.0]: https://github.com/kgdunn/process-improve/compare/v1.55.1...v1.56.0
Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ authors:
repository-code: "https://github.com/kgdunn/process-improve"
url: "https://kgdunn.github.io/process-improve/"
license: MIT
version: 1.58.0
version: 1.59.0
date-released: "2026-07-23"
keywords:
- chemometrics
Expand Down
13 changes: 10 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "process-improve"
version = "1.58.0"
version = "1.59.0"
description = 'Designed Experiments; Latent Variables (PCA, PLS, multivariate methods with missing data); Process Monitoring; Batch data analysis.'
readme = "README.md"
license = "MIT"
Expand Down Expand Up @@ -116,7 +116,7 @@ dev = [
"pytest-cov>=7.0.0",
"pytest-xdist>=3.8.0",
"pydata-sphinx-theme>=0.16.1",
"ruff>=0.11.0",
"ruff>=0.11.0,<0.16", # upper-bound so a future ruff release cannot silently break the lint gate (see mypy)
"sphinx>=8.1.3",
"nbsphinx>=0.9.5",
"ipykernel>=6.29",
Expand Down Expand Up @@ -158,7 +158,7 @@ dev = [
"pytest-cov>=7.0.0",
"pytest-xdist>=3.8.0",
"pydata-sphinx-theme>=0.16.1",
"ruff>=0.11.0",
"ruff>=0.11.0,<0.16", # upper-bound so a future ruff release cannot silently break the lint gate (see mypy)
"sphinx>=8.1.3",
"nbsphinx>=0.9.5",
"ipykernel>=6.29",
Expand Down Expand Up @@ -208,6 +208,13 @@ exclude = [
# Enable ALL Pyflakes codes by default.
select = ["ALL"]
ignore = [
# Copyright notices are managed in the repo's own "# (c) Kevin Dunn" house
# style, not via ruff. CPY001 is a preview rule with a default regex that
# requires the literal word "Copyright"; once a ruff release promoted it to
# stable it fired on nearly every file. Keep it off so the lint gate does not
# depend on the installed ruff version.
"CPY001", # Missing copyright notice at top of file.

"C408", # Unnecessary <dict/list/tuple> call - rewrite as a literal. Disagree with this.

# Arguments
Expand Down
31 changes: 31 additions & 0 deletions src/process_improve/datasets/multivariate/cheddar-cheese.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Case,Acetic,H2S,Lactic,Taste
1,4.543,3.135,0.86,12.3
2,5.159,5.043,1.53,20.9
3,5.366,5.438,1.57,39
4,5.759,7.496,1.81,47.9
5,4.663,3.807,0.99,5.6
6,5.697,7.601,1.09,25.9
7,5.892,8.726,1.29,37.3
8,6.078,7.966,1.78,21.9
9,4.898,3.85,1.29,18.1
10,5.242,4.174,1.58,21
11,5.74,6.142,1.68,34.9
12,6.446,7.908,1.9,57.2
13,4.477,2.996,1.06,0.7
14,5.236,4.942,1.3,25.9
15,6.151,6.752,1.52,54.9
16,6.365,9.588,1.74,40.9
17,4.787,3.912,1.16,15.9
18,5.412,4.7,1.49,6.4
19,5.247,6.174,1.63,18
20,5.438,9.064,1.99,38.9
21,4.564,4.949,1.15,14
22,5.298,5.22,1.33,15.2
23,5.455,9.242,1.44,32
24,5.855,10.199,2.0,56.71
25,5.366,3.664,1.31,16.8
26,6.043,3.219,1.46,11.6
27,6.458,6.962,1.72,26.5
28,5.328,3.912,1.25,0.7
29,5.802,6.685,1.08,13.4
30,6.176,4.787,1.25,5.5
104 changes: 104 additions & 0 deletions src/process_improve/datasets/multivariate/solvents.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
Chemical,MeltingPoint,BoilingPoint,Dielectric,DipoleMoment,RefractiveIndex,ET30,Density,logP,Solubility
W01_WATER,0,100,78.3,5.9,1.333,63.1,0.9982,-1.38,1.774
G02_Formamide,2.55,210.5,111,11.2,1.4475,56.6,1.1334,-1.51,1.401
A03_1.2-Ethanediol__,-12.6,197.15,37.7,7.7,1.4318,56.3,1.1088,-1.36,1.06
A04_Methanol,-97.7,64.5,32.66,5.7,1.3284,55.4,0.7914,-0.77,1.393
G05_N-Methyl_formamid,-3.8,182.5,182.4,12.9,1.4319,54.1,1.01,-1.3,1
A06_diethylenglycol,-7.8,245.7,31.69,7.7,1.4475,53.8,1.109,1,1
A07_triethylenglycol,-4.3,288,23.69,10,1.4558,53.5,1.0682,-1.33,0.8521
A08_2-Methoxyethanol,-85.1,124.6,16.93,6.8,1.4021,52.3,0.965,-0.77,1.103
G10_N-Methylacetamid,30.6,206.7,191.3,14.2,1.4253,52,0.957,-1.05,1
A11_Ethanol,-114.5,78.3,24.55,5.8,1.3614,51.9,0.785,-0.31,1.231
A12_2-AminoEthanol,10.5,170.95,37.72,7.6,1.4545,51.8,1.018,-1.31,1.216
F13_Acetic_acid,16.7,117.9,6.17,5.6,1.3719,51.7,1.0492,-0.17,1.002
A15_Benzylalcohol,-15.3,205.45,13.1,5.5,1.5404,50.4,1.042,1.1,-0.992
A14_1-Propanol,-126.2,97.15,20.45,5.5,1.3856,50.7,0.804,0.25,1.126
A16_1-butanol,-88.6,117.7,17.51,5.8,1.3993,50.2,0.8098,0.88,0.125
A19_2-methyl_1-propa,-108,107.9,17.93,6,1.3959,48.6,0.794,0.76,0.176
A20_2-propanol,-88,82.2,19.92,5.5,1.3772,48.4,0.786,0.05,1.117
A21_2_butanol,-114.7,99.5,16.56,5.5,1.3971,47.1,0.808,0.61,0.176
A18_3-methyl_1-butan,-117.2,130.5,15.19,6.1,1.4072,49,0.8092,1.42,-0.518
A22_cyclohexanol,25.15,161.1,15,6.2,1.4648,46.9,0.962,1.23,-0.755
C23_4-methyl-1.3-dio,-54.5,241.7,64.92,16.5,1.4215,46.6,1.204,1,1
A24_2-pentanol,1,119,13.71,5.5,1.4064,46.5,0.81,1.19,-0.612
L25_Nitromethane,-28.55,101.2,35.94,11.9,1.3819,46.3,1.137,-0.35,0.235
N27_Acetonitrile,-43.8,81.6,35.94,11.8,1.3441,45.6,0.7857,-0.34,1.282
A26_3-pentanol,-75,115.3,13.35,5.5,1.4104,45.7,0.821,1.21,-0.234
X28_dimethylsulfoxid,18.5,189,46.45,13.5,1.4793,45.1,1.101,-1.35,1
+29_aniline,-6,184.4,6.71,5,1.5863,44.3,1.0217,0.9,-0.37
X30_Sulfolane,28.45,287.3,43.3,16,1.4816,44,1.262,-0.77,1
F31_acetic_anhydride,-73.1,140,20.7,9.4,1.3904,43.9,1.082,1,0.07
A35_t-butanol,25.6,82.3,12.47,5.5,1.3877,43.3,0.789,0.35,-1.057
G32_N-N-dimethylform,-60.4,153,36.71,10.8,1.4305,43.8,0.945,-1.01,1.112
G33_N-N-dimethylacet,-20,166.1,37.78,12.4,1.4384,43.7,0.937,-0.77,1.11
N34_propannitrile,-92.8,97.35,28.86,11.7,1.3658,43.7,0.782,0.16,1
G37_1-methyl-pyrroli,-24.4,202,32.2,13.6,1.47,42.2,1.026,1,1
K38_Acetone,-94.7,56.1,20.56,9,1.3587,42.2,0.79,-0.24,1.134
L44_nitrobenzene,5.8,210.8,34.78,13.3,1.5562,41.2,1.204,1.85,-1.778
N41_Benzonitrile,-12.75,191.1,25.2,13.4,1.5282,41.5,1.01,1.56,-1.013
+40_1.2.Diaminoethan,11.3,116.9,12.9,6.3,1.4568,42,0.899,-2.04,1
H42_1.2-Dichloroetha,-35.7,83.5,10.7,6.1,1.4448,38,1.235,1.48,-1.092
A45_2-methyl-2-butan,-8.8,102,5.78,5.7,1.405,41.1,0.806,0.89,0.039
K43_2-butanone,-86.7,79.6,18.51,9.2,1.3788,41.3,0.805,0.29,0.494
K52_Acetophenone,19.6,202,17.39,9.8,1.5342,40.6,1.0281,1.58,-1.553
H51_dichlormethane,-94.9,39.6,8.93,5.2,1.4242,40.7,1.33,1.25,-0.699
U47_1.1.3.3.tetramet,-1.2,175.2,23.6,11.7,1.4493,41,0.969,0.19,1
G49_HMPT,7.2,233,29.6,18.5,1.4588,40.9,1.024,0.28,1
K55_cyclohexanone,-32.1,155.65,16.1,10.3,1.451,39.8,0.9478,0.81,-1.236
+53_pyridine,-41.55,115.25,12.91,7.9,1.5102,40.5,0.982,0.65,1.094
E54_Methylacetate,-98.05,56.9,6.68,5.7,1.3614,40,0.933,0.18,0.486
K56_4-methyl-2-penta,-84.7,117.4,13.11,2.7,1.3958,39.4,0.7978,1.31,-0.871
H57_1.1.-dichloroeth,-97,57.3,10,6.1,1.4164,39.4,1.176,1.79,-1.259
+58_Quinoline,-14.85,237.1,8.95,7.3,1.6273,39.4,1.093,2.03,1
K59_3-pentanone,-39,102,17,9.4,1.3923,39.3,0.8138,0.91,-0.27
H60_chloroform,-63.15,61.2,4.81,3.8,1.4459,39.1,1.48,1.97,-1.246
T62_Trietylenglycold,-45,216,7.5,1,1.4224,38.9,1,1,1
T64_dietylenglycoldi,-64,159.8,5.8,6.6,1.4078,38.6,1,1,1
T65_1.2-Dimethoxyeth,-69,84.5,7.2,5.7,1.3796,38.2,0.8629,-0.21,1
H67_1.2.Dichlorobenz,-17,180.5,9.93,7.1,1.5515,38,1.305,3.38,-3.029
E66_ethylacetate,-83.55,77.1,6.02,6.1,1.3724,38.1,0.9,0.73,-0.041
H73_fluorobenzene,-42.2,84.7,5.42,4.9,1.4684,37,1.023,2.27,-1.797
H78_Iodobenzene,-31.35,188.3,4.49,4.7,1.62,36.2,1.831,3.25,-2.847
H75_Chlorobenzene,-45.6,131.7,5.62,5.4,1.5248,36.8,1.106,2.84,-2.45
H76_Bromobenzene,-30.8,155.9,5.4,5.2,1.5568,36.6,1.495,2.99,-2.57
T70_tetrahydrofuran,-108.4,66,7.58,5.8,1.4072,37.4,0.889,0.46,1.142
T71_methoxybenzene,-37.5,153.6,4.33,4.2,1.517,37.1,0.996,2.11,-1.917
T77_Ethoxybenzene,-29.5,169.8,4.22,4.5,1.5074,36.6,0.967,2.51,-2.332
H79_1.1.1-Trichloret,-30.4,74.1,7.25,5.7,1.438,36.2,1.339,2.49,-2.179
T80_1.4-Dioxane,11.8,101.3,2.21,1.5,1.4224,36,1.034,-0.27,1.07
H81_Trichlorethene,-86.4,87.2,3.42,2.7,1.4773,35.9,1.464,2.42,-1.995
+83_Piperidine,-10.5,106.2,5.8,4,1.4525,35.5,0.861,0.84,1.005
T85_Diphenylether,26.9,258.1,3.69,3.9,1.5763,35.3,1.075,4.21,-3.947
T86_Diethylether,-116.3,34.4,4.2,3.8,1.3524,34.5,0.714,0.89,0.076
D87_Benzene,5.5,80.1,2.27,0,1.5011,34.3,0.8786,2.13,-1.559
T73_Diisopropylether,-85.5,68.3,3.88,4.2,1.3681,34,0.7241,1.52,-1.982
D89_toluene,-95,110.6,2.38,1,1.4969,33.9,0.867,2.73,-1.747
T75_Din-butylether,-95.2,140.3,3.08,3.94,1.3992,33,0.7689,3.21,-1.941
+94_triethylamine,-114.7,88.9,2.42,2.9,1.401,32.1,0.7275,1.45,0.224
D77_1.3.5-trimethylb,-44.7,164.7,2.28,0,1.4994,32.9,0.865,3.42,-3.356
S92_Carbondisulfide,-111.6,46.2,2.64,0,1.6275,32.8,1.263,1.94,-1.552
H93_carbontetrachlor,-22.8,76.6,2.23,0,1.4602,32.4,1.59,2.83,-2.294
H80_Tetracloroethyle,-22.4,121.2,2.3,0,1.5057,31.9,1.623,3.4,-2.895
D100_Cyclohexane,6.7,80.7,2.02,0,1.4262,30.9,0.778,3.44,-3.069
D98_n-Hexane,-95.3,68.7,1.88,0,1.3749,31,0.66,3.98,-3.359
A09_tetraethylenegly,-6.2,327.3,19.7,10.8,1.4577,52.2,1.1285,1.38,1
A17_1-Pentanol,-78.2,138,13.9,5.7,1.41,49.1,0.8144,1.56,-0.631
U36_DMEU,8.2,225.5,37.6,13.6,1.4707,42.5,1,1,1
U_39_DMPU,-20,230,36.12,14.1,1.4881,42.1,1,1,1
K46_2-Pentanone,-76.9,102.3,15.38,9,1.3908,41.1,0.8089,0.91,-0.328
+48_Morpholine,-4.8,128.9,7.42,5.2,1.4542,41,1.0005,-0.86,1.06
K50_3-Methyl2-butano,-92,94.2,15.87,9.2,1.388,40.9,0.8051,0.56,-0.164
K61_3.3-Dimethyl-2-b,-49.8,106.3,13.1,9.3,1.3952,39,0.8012,0.85,-0.706
K63_2.4-Dimethyl-3-p,-69,125.25,17.2,9.1,1.3999,38.7,0.8108,1.49,-1.299
K68_2.6-Dimethyl4-he,-46,168.2,9.91,8.9,1.4122,38,0.8053,2.96,-1.731
T69_dietylenglycolDi,-44.3,188.9,5.7,1,1.4115,37.5,0.9063,1,0.076
C72_Diethylcarbonate,-43,126.8,2.82,3,1.3837,37,0.9752,1.21,-0.798
H74_1.1-Dichlorethen,-122.6,31.6,4.82,4.3,1.4247,37,1.218,1.86,-1.666
T82_t-Butyl-methylet,-108.6,55.2,4.5,4.1,1.369,35.5,0.7405,0.94,1
+84_Diethylamine,-49.8,55.55,3.78,4,1.3846,35.4,0.7056,0.58,1
T88_di-n-propylether,-123.2,90.1,3.39,4.4,1.3805,34,0.736,2.03,-1.388
D90-p-Xylene,13.3,138.4,2.27,0,1.4958,33.1,0.8611,3.15,-2.806
+95_Tri_n-butylamine,-70,214,1,2.6,1.4291,32.1,0.7771,4.56,-3.116
D96_cis-decalin,-43,195.8,2.2,0,1.481,31.2,0.8965,5.08,-5.192
D97_n-Heptane,-90.6,98.4,1.92,0,1.3876,31.1,0.68376,4.57,-4.046
D99_n-Pentane,-129.7,36.1,1.84,0,1.3575,31,0.6262,3.39,-3.129
2 changes: 2 additions & 0 deletions src/process_improve/multivariate/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Multivariate analysis: PCA, PLS, TPLS, scaling, and diagnostic plots."""

from process_improve.multivariate.methods import (
OPLS,
PCA,
PLS,
TPLS,
Expand Down Expand Up @@ -31,6 +32,7 @@
)

__all__ = [
"OPLS",
"PCA",
"PLS",
"TPLS",
Expand Down
Loading