What’s New in Revision 0.11.0
These are the changes in SpectroChemPy-0.11.0. See Release notes for a full changelog, including other versions of SpectroChemPy.
New Features
MCRALS: added theconstraints=parameter that accepts a list of publicConstraintobjects (NonNegative,Unimodal,Closure,Monotonic,ModelProfile, …). Both the newconstraints=API and the legacy traitlet-based parameters now route through the same internal pipeline: the publicConstraintobjects have become the canonical intermediate representation. Mixed usage (legacy traitlets +constraints=at the same time) raises a clearValueError. The legacy traitlet-basedAPI remains fully supported for backward compatibility. (PR #1383)
AnalysisResult(PCA, NMF, PLS, MCR‑ALS, …) andFitResultnow have a rich_repr_html_for Jupyter notebooks, with collapsible Parameters, Outputs, and Diagnostics sections and recursive rendering of embedded NDDataset objects using their native HTML representation. The text__repr__and the public API are unchanged. (PR #1299)Peak analysis workflows are now easier to inspect and validate:
find_peaks(..., as_result=True)returns a lightweightPeakFindingResultobject withpeaks,properties, atableview,to_dict(),to_csv(),sort_by(),head(),top(), andcolumn()helpers, whileOptimize.validate_script(script=None)can validate a curve-fitting script before launching the optimisation and returns structuredScriptErrordiagnostics. The defaultfind_peaks()return value remains the historical(peaks, properties)tuple, and the new helpers do not add a pandas dependency. (PR #1351)The analysis user guide now includes a reference peak-analysis workflow tutorial connecting peak detection,
PeakFindingResult,PeakTable, CSV export, manual script writing, script validation, andOptimize.fit()in one end-to-end example. TheOptimizeuser-facing documentation now also clarifies method selection (least_squares,leastsq,simplex,basinhopping), the meaning ofFitResult.parametersas run configuration, and the lightweightvalidate_constraints()/constraintsnormalization surface for constraint validation before fit.FitResultfromOptimize.resultnow exposes a residual dataset asresult.outputs["residuals"]/result.residualsand basic fit-quality diagnostics underresult.diagnostics:rss/sse,rmse,r_squared,n_observations,n_varying_parameters,degrees_of_freedom,reduced_chi_square, andadjusted_r_squared, plus normalized solversuccess,status, andmessagefields. (PR #1365)Optimizenow retains the raw least-squares Jacobian onopt.jacobianwhen a backend naturally provides one, while methods without a native Jacobian exposeNone. (PR #1369)FitResult.covariancenow exposes an approximate local least-squares covariance matrix for the fitted varying parameters, derived from the Jacobian when available and residual degrees of freedom are positive.FitResult.varianceandFitResult.stderrexpose the covariance diagonal and corresponding approximate parameter standard errors, andFitResult.correlationexposes the corresponding parameter-correlation matrix through the same availability rules. (PR #1373, PR #1375)FitResult.confidence_intervalsnow exposes approximate two-sided 95% confidence intervals for the fitted varying parameters, derived from the fitted values, standard errors, and Student-t critical values using the residual degrees of freedom. (PR #1376)FitResult.diagnosticsnow exposes Gaussian-residual model-comparison criteriaaicandbicderived from the residual sum of squares, observation count, and effective varying-parameter count. (PR #1378)Existing
result.fittedandresult.componentsbehavior is preserved.SpectroChemPy now exposes top-level helpers for common 1D line shapes:
scp.polynomial(...),scp.gaussian(...),scp.lorentzian(...),scp.voigt(...),scp.asymmetricvoigt(...), andscp.sigmoid(...). The line-shape helpers exceptscp.sigmoidalso acceptnormalized=Falseto return a profile whose peak amplitude is exactlyampl. Common mathematical helpers are also available at top level:scp.exp(...),scp.log(...),scp.log10(...),scp.sin(...), andscp.cos(...). Synthetic profile creation also gainsscp.normal(...)for native Gaussian noise generation without dropping tostack(..., axis=1)is now supported for stacking 1D profiles as columns into a 2D dataset. This makes the workflow for building synthetic concentration or profile matrices fully native within the SpectroChemPy API, without falling back tonp.column_stack(...)or manualNDDatasetwrapping. (PR #1309, PR #1310)Reading multi-object files such as MATLAB
.matfiles, multi-subfile SPC files, and ZIP archives now returns aScpObjectListresult with helper methods for selecting datasets by size, name, dimensionality, or shape. TheScpObjectListalso gaineddataset_by_ndim(),dataset_by_name(),datasets_by_shape()and other selection helpers. The list-like__getitem__/__len__interface is unchanged, and the new helpers do not add new dependencies. (PR #1306, PR #1362)SpectroChemPy now has a fuller preprocessing API for chemometric workflows: standard operations such as
normalize(),center(),autoscale(),snv(),msc(),pareto_scale(),range_scale(),robust_scale(), andlog_transform()are available as first-classNDDatasetmethods, and matching stateful transformer classes (CenterTransformer,AutoscaleTransformer,SNVTransformer,NormalizeTransformer,MSCTransformer,ParetoScaleTransformer,RangeScaleTransformer,RobustScaleTransformer,LogTransformer) supportfit(),transform(),fit_transform(), andinverse_transform()where applicable. The transformers also exposeget_params()/set_params(**params)and a clear__repr__, enabling safer reuse on new data and easier parameter inspection or cloning. (PR #1339, PR #1344, PR #1345)2D
plot(method="lines"/"stack")now automatically uses coordinate labels as matplotlib line labels, so thatax.legend()shows meaningful names without needing to pass labels explicitly. Legend entries are displayed in natural (first-to-last) order. (PR #1320)Added top-level
scp.polynomial(...)andscp.normal(...)helpers for synthetic spectral profile generation, completing the set of accessible line-shape helpers. (PR #1360)
Bug Fixes
FIX: The
Optimizescript parser now rejects three categories of previously-silent misconfiguration:MODEL:blocks without ashape:definition,shape:lines that appear before anyMODEL:declaration, and duplicateMODEL:labels (which historically overwrote the first model’s parameters). These scripts could never produce valid fit results, and the parser now reports them as explicit errors instead of silently accepting them. (PR #1386)WiRE (
.wdf) reader no longer attaches YLST data as a confusing auxiliarymcoordinate incoordset. The YLST data is now stored ondataset.meta(asylst_data,ylst_title,ylst_units) where it belongs as per-spectrum metadata. (PR #1332)MCRALScorrectness fixes (PR #1340, PR #1363): emptyclosureConcno longer runs a wasteful closure block (PR1 B4); single-component closure targets (closureConc=[0],closureConc="all") are now honoured instead of being silently disabled bynp.any(##911);normSpec='max'/'euclid'with a zero-norm spectrum no longer producesnan/inf(PR1 B9);getConc/getSpecdispatch now correctly usesargsGetSpec/kwargsGetSpecinstead of theargsGetSpecctypo (PR1 B1) and accepts bare-profile, 2-tuple(profiles, new_args), and 3-tuple(profiles, new_args, extra)returns (PR1 B2);getSt_to_St_idxwithNoneentries no longer crashes in the validator’smax()call (PR1 B5);_unimodal_1Dno longer infinite-loops or indexes out of bounds for pathological tolerances (tol < 1, includingtol == 1.0) while remaining byte-identical in the documented regime (tol >= 1.1) (PR1 B6);monoIncTolandmonoDecTolare now documented asFloattraits (PR1 B7/B8). The samenp.anycomponent-selection anti-pattern that caused ##911 in_ClosureConstrainthas now been fixed across the remaining constraint activation guards: selecting only component 0 withnonnegConc=[0],nonnegSpec=[0],unimodConc=[0],unimodSpec=[0],monoIncConc=[0],monoDecConc=[0],hardConc=[0]orhardSpec=[0]no longer silently disables the constraint (the guards now use an explicit truthiness test instead ofnp.any(...), sincenp.any([0])evaluates toFalse).Plotting behavior is more consistent again: scatter plots now show markers as expected, single-dataset
plot_multiple()/multiplot()calls preserve the requested plotting method, 1D and 2D artists honor more style keywords consistently, the experimental Plotly backend has been removed (use_plotlyis no longer available), and legacylines/penaliases continue to work across dimensional fallbacks. (PR #1381, PR #1384)plotmerit()/plot_compare()are now clearer for fit inspection: the reconstructed trace remains visible even for near-perfect overlaps, the historicalkind="scatter"/method="scatter"options now produce real marker-based rendering,nb_tracesis honored in the current plotting path, andoffsetonce again separates the residual trace from the main signal to improve notebook readability. (PR #1377)Fixed several processing regressions and edge cases: multi-dimensional ZPD detection in interferogram apodization is more reliable,
rs(),ls(), androll()now shift multi-dimensional data along the correct axis,denoise()validates its 2D input correctly. (PR #1352)Preprocessing transformers no longer emit a traitlets dtype warning when operating on
MaskedArrayinputs — the internal cast now usesnp.asarray()instead ofnp.array()to preserve the plain-ndarray contract while silencing the warning. (PR #1348)Fixed several processing regressions and edge cases:
npy.dot()now checks the correct operand type and honours thestrictargument. (PR #1352)PLSRegressionnow works with a 1DNDDatasetas the response variabley. This fixes failures inpredict(),y_scores,y_loadings,y_weights,y_rotations,result, andcoefwhen fitting with a 1D target. (PR #1305)Optimize._parsingno longer infinite-loops when a numpy name (e.g.pi,sin,cos) appears in a reference expression. The oldstr.replacere-matched the token inside thenp.-prefixed replacement, causingpi → np.pi → np.np.pi → …. Fixed by usingre.subwith a negative lookbehind assertion. (PR #1397)
Dependency Updates
Deprecations
The historical internal import path
spectrochempy.processing.alignementis superseded by the canonicalspectrochempy.processing.alignmentnamespace. The publicscp.align(...)anddataset.align(...)APIs are unchanged. The old path remains available as a deprecated compatibility alias, scheduled for removal in0.12.0. (PR #1372)Plotting
method="stack"andmethod="map"aliases are deprecated. Use the canonicalmethod="lines"(for 1D) or let the dispatcher choose automatically. The aliases will be removed in0.12.0.The
force_stackkeyword argument in concatenation functions is deprecated in favour of the unqualifiedmethod="stack"call. It will be removed in0.12.0.
Developer
MAINT: Moved the Optimize backend onto the canonical
_FitModelSpecrepresentation for model preparation, solver vectorization/restoration, diagnostics, and result parameter extraction while keepingFitParametersas a compatibility layer. The preferred execution path now resolves references throughprepare_model()before numerical optimization, preserving the existing fit behaviour and public API. The parser now also produces the canonical model representation first and only derivesFitParametersfor legacy compatibility surfaces such asOptimize.fpand the historical parser return value. (PR #1392, PR #1393)MAINT: Extracted parameter-space transforms from
FitParametersinto standalone_to_internal/_to_externalutilities (_parameter_transform.py). Two historical bugs fixed:lob is not None→upb is not None,pei = pi→pei = item. (PR #1388, PR #1389)MAINT: Added private
_FitModelSpecstructured model-definition object withfrom_fitparameters(fp)constructor,to_script()serializer, andcount_varying()/extract_varying_values()inspection methods._ComponentParamsViewadapter decouplesgetmodel()from the{param}_{label}parser convention by duck-typing. Strategy B of the Optimize convergence plan — three phases delivered: parameter transform extraction, structured model spec, component-params adapter. (PR #1387, PR #1390)MCRALS: introduced the public constraint API skeleton (spectrochempy.analysis.decomposition.mcrals_constraints). The new public classesConstraint(abstract base),NonNegative,Closure,Unimodal,Monotonic,ZeroRegion,Selectivity,FixedValues,ReferenceProfileandModelProfiledescribe scientific prior knowledge about the concentration ("C") or spectral ("St") profiles ofMCRALSas declarative, first-class objects. All ten classes are importable from the
spectrochempy.analysis.constraintssubmodule (from spectrochempy.analysis import constraints), registered in the public API reference, and covered by dedicated construction / validation/ equality / repr / model / tolerance tests (
tests/test_analysis/test_decomposition/test_mcrals_constraints.py). This PR introduces the vocabulary and public surface only: the classes are data containers and validators, they are not yet connected to the internal constraint engine, and using them does not change the behaviour ofMCRALS.fit. No public APIs and no numerical behaviour change. Connection to the internal engine, the legacy traitlet converter, and the actual enforcement implementations are the subject of subsequent PRs (see the project RFC for the roadmap).MCRALS: added a behavioral characterization test matrix (tests/test_analysis/test_decomposition/test_mcrals.py) freezing the current numerical output ofMCRALSacross the documented constraint (non-negativity, unimodality, monotonicity, closure, spectral normalization, hard-generated profiles), solver (lstsq,nnls,pnnls) and initialization (Cvs.St) space, ahead of the next structural refactoring. The tests use a tiny deterministic synthetic dataset and compare against fixed expected arrays. The matrix initially exposed the samenp.any([0])component-selection anti-pattern that affected issue #911 in_ClosureConstraint(selecting only component 0 withnonnegConc=[0]/monoIncConc=[0]/monoDecConc=[0]/hardConc=[0]/hardSpec=[0]silently disabled the constraint); these are now fixed (see the Bug Fixes section above) and frozen as passing regression tests. SeeMCRALS_PR4_BEHAVIOR_TESTS.mdfor the characterization report.MCRALS: internal architecture overhaul, no public API or numerical behavior change.MCRALS._fitis now structured around an internal constraint engine: the public traitlets (nonnegConc,unimodConc,monoIncConc/monoDecConc,closureConc,normSpec,hardConc/hardSpecand their spectral counterparts) are translated once per fit into a private, ordered list of_Constraintobjects, and the ALS loop iterates over them rather than calling each constraint by name. All constraint classes are private (_-prefixed) and not exported in__all__; the constraint order, the in-place vs. copy semantics, the traitlets, thefit/transform/fit_transform/inverse_transformsignatures, the_outfitreturn tuple, and thegetConc/getSpecexternal-generator contract are preserved byte-for-byte. This refactoring reduces_fitfrom a ~230-line monolith to a high-level loop over constraint pipelines and prepares the ground for future work (constraint pipelines, generated-profile abstraction, multi-criteria convergence, scikit-learn compatibility).Centralized internal plotting kwargs normalization in a private helper module to reduce duplication across plotting entry points while preserving public plotting aliases and rendering behavior. (PR #1370)
Centralized internal plotting method normalization in a private helper module to reduce duplication across backend dispatch, multiplot handling, and 1D/2D fallback validation, without changing the public plotting API. (PR #1370)
CI: Added a PR compliance workflow (
pr-compliance.yml) that automatically checks: (1) the PR title starts with a valid prefix listed inCONTRIBUTING.md(read dynamically so the list stays single-source), (2)docs/sources/whatsnew/changelog.rsthas been updated. Both checks can be bypassed via the labelsnon-standard-prefixandno-changelogrespectively. The PR-title prefix extraction now tolerates Markdown table spacing inCONTRIBUTING.md, and Dependabot PRs are exempt from both the prefix and changelog checks so automated dependency updates do not require manual title edits or labels.DEV: Added a
commit-msgpre-commit hook (check_commit_prefix) that reads the allowed prefixes directly fromCONTRIBUTING.mdand rejects any commit whose subject line does not start with one of them. Merge commits and reverts are automatically exempt. Usegit commit --no-verifyto bypass locally if necessary.MAINT: Refactored all nine procedural preprocessing functions (
normalize,center,autoscale,snv,msc,pareto_scale,range_scale,robust_scale,log_transform) to internally delegate to their transformer counterparts. This eliminates code duplication between the procedural and transformer APIs while preserving full backward compatibility, includinginplacebehaviour and history messages. (PR #1344)CI: Moved archived stable docs builds (oldest supported + recent versions) out of the per-push
build_docs.ymlworkflow into a dedicated weeklybuild_docs_archived_versions.ymlworkflow. This shrinks the main deployment artifact (~382 MB previously) and avoids GitHub Pagessyncing_filestimeouts caused by oversized bundles. The main workflow now builds only the current version (plus preview docs for documentation branches). Addedtimeout-minutes: 360to both workflows andcontinue-on-error: trueon archived-version steps so a single failing old-tag build does not block the rest. (PR #1350)DOC: Refreshed the installation guide across all platforms: added
uvas the recommended installation method, created a “Choosing an installation method” decision table, replaced deprecated Mambaforge references with Miniforge, and updated contributor setup instructions to match the project’s ownuv-based development toolchain. Conda/mamba and pip remain documented as alternatives. (PR #1368)