Repository navigation
Conversation
The knot splines, the CART / LightGBM location selectors and the Preprocessor
config never checked task, and the selectors treated every value other than
"regression" as classification. A typo such as task="Regression" therefore gave
classification-tree knots, failed deep inside scikit-learn on a float target
("Unknown label type: continuous"), fit a continuous target with LightGBM's
binary objective, and made every preset resolve numerical_method to "ple".
A shared validate_task helper in pretab.core.parameters now checks task at fit
in the B/M/I, natural cubic and cubic regression splines (None still means
"regression"), PLE, the feature maps, CrossFittedTransformer, the location
selectors and PreprocessorConfig.from_params. Preprocessor validates it before
preset resolution, so fit and get_resolved_config raise for any method or
preset; the selectors dispatch explicitly on "classification".
All of them raise InvalidParamError with one message
("<Estimator>.task = 'Regression' is invalid. ..."), replacing the differing
PLE, feature-map and CrossFittedTransformer wording.
Fixes #93
This was referenced Oct 16, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #93.
An invalid
tasksuch as the typo"Regression"was accepted by the knot splines, thePreprocessorconfiguration and the location selectors, and silently treated as classification. Users got classification-tree knots, LightGBM'sbinaryobjective on a regression target, an unrelated scikit-learn "Unknown label type: continuous" error on a float target, or the classification preset method"ple".Root cause
taskwas validated only byPLETransformer, the feature maps andCrossFittedTransformer, each with its own message.BaseLocationSelector.selecttooktask or "regression"unchecked, and the CART and LightGBM selectors dispatched withif task == "regression": ... else: <classification>, so every other value took the classification branch.PreprocessorConfig.from_paramsdid not checktask, and_preset_numerical_methodreturned"ple"for anything but"regression".Fix
validate_task(task, estimator, *, allow_none=False)andTASKSinpretab/core/parameters.py. The match is exact, and an invalid value raisesInvalidParamErrorthroughinvalid_param_error, so every estimator reports it in the same format.validate_taskruns atfitinBaseSplineTransformer(B-, M- and I-splines),NaturalCubicSplineTransformerandCubicRegressionSplineTransformer(withallow_none=True;Nonestill means regression).PLETransformer,BaseCenterExpansion(the feature maps) andCrossFittedTransformer._fit_fulluse it instead of their own checks.BaseLocationSelector.selectvalidatestask(Noneis treated as"regression").CARTLocationSelectorandLightGBMLocationSelectornow choose the classifier tree and thebinaryobjective only fortask == "classification".Preprocessor._resolved_paramsvalidatestaskbefore a preset derivesnumerical_methodfrom it, sofitandget_resolved_config()both raise.PreprocessorConfig.from_paramsvalidates it as well.Behaviour changes
taskother than"regression"or"classification"raisesInvalidParamError: atfitof every transformer that takestask, in the location selectors, and inPreprocessor.fit/get_resolved_config(). ThePreprocessorraises even when the chosen method ignores the task (for exampleminmax).Preprocessor(task=None)now raises. Before, the target-aware splines treated it as regression and untargeted methods ignored it, whilepleand every preset rejected it with PLE's error. The standalone splines and the location selectors still acceptNoneas"regression".PLETransformer, the feature maps (rbf,relu,sigmoid,tanh) andCrossFittedTransformerchange to the shared format, for examplePLETransformer.task = 'Regression' is invalid.followed by the constraint and the valid values. The exception type staysInvalidParamError.pretab.core.parameters.validate_taskand constantTASKS. Validtaskvalues give the same results as before.Documentation
docs/core_concepts/configuration.md: the presets paragraph says thattaskis matched exactly and that any other value, such as"Regression", raises anInvalidParamErrorfromfitandget_resolved_config()instead of falling back to the classification preset. The "validated atfittime" warning lists an invalidtask.validate_task;BaseLocationSelector.select(task, newRaises); thepretab/compose/config.pymodule docstring,PreprocessorConfigand itsfrom_paramsRaises; thePreprocessortaskparameter, andRaisessections for_resolved_paramsandget_resolved_config;_preset_numerical_method; thetaskparameter ofBaseSplineTransformer,CubicRegressionSplineTransformerandNaturalCubicSplineTransformer.Tests
test_invalid_task_raises[Regression, classif, None](tests/compose/test_config.py):PreprocessorConfigrejects each value. Fails onmain, passes here.test_selectors_reject_an_unknown_task(CART and LightGBM, each with"Regression","regresion",""and1):selectraisesInvalidParamError. Fails onmain, passes here.test_unknown_task_raises_at_fit[bspline, cubicspline, ispline, mspline, naturalspline, ple, rbf, relu, sigmoid, tanh]: every registered method with ataskparameter raises the shared message atfitfortask="Regression". Fails onmain, passes here; the knot splines fitted there, andpleand the feature maps raised with their old messages.test_preprocessor_unknown_task[bspline, naturalspline, rbf, ple, minmax]:Preprocessor(task="Regression").fitraisesPreprocessor.task = 'Regression' is invalid. Fails onmain, passes here.test_invalid_task_raises_instead_of_resolving[None, standard, expanded, adaptive](tests/integration/test_presets.py):get_resolved_config()raises instead of resolving a method. Fails onmain, passes here.test_ple_invalid_task_raises,test_ple_unsupported_task,test_rbf_invalid_task,test_relu_invalid_task,test_sigmoid_invalid_taskandtest_tanh_invalid_task_raises: updated to the shared message. They fail onmainonly because of the wording.test_cart_task_none_means_regressionandtest_spline_task_none_places_knots_for_regression:task=Nonestill gives the regression locations and knots. Guard tests: pass on both by design.Reproduction
Output of the script from #93 on this branch, with each call wrapped separately. The last line is an added check that a valid
taskstill resolves the preset as before:Checks
just test: 1470 passed, 59 skipped, 7 xfailed; coverage 93.66% (gate 90%).scripts/quickstart.pypasses.just docs: strict build succeeds.just check: all pre-commit and pre-push hooks pass (ruff format, ruff lint, prettier, file hygiene, pyright with 0 errors).Merge notes
main.pretab/core/selectors.pyin two places. In the imports, Target-aware location selectors return too few, duplicate, or boundary locations on tied/discrete features #57 addsuniform_knotsto the.knotsimport and this PR addsfrom .parameters import validate_task. In the docstring ofBaseLocationSelector.select, Target-aware location selectors return too few, duplicate, or boundary locations on tied/discrete features #57 rewrites theReturnstext and this PR adds aRaisessection. Keep both sides in each.LightGBMLocationSelector._ordered_candidates(label encoding,binaryormulticlass). Keep LightGBM placement with task="classification" always uses the binary objective on raw labels #61'sobjectiveblock and**objective, and turn its condition around so that it dispatches on"classification"(if task == "classification": <label encoding> else: <regression>), as this PR does for the inline conditionals it replaces.pretab/core/selectors.pyin the imports (keep bothvalidate_taskand A 2-D y (multi-output target) crashes the B/M/I splines even when unsupervised, and target-aware methods with unrelated errors #88'ssingle_target) and in theselectdocstring (take A 2-D y (multi-output target) crashes the B/M/I splines even when unsupervised, and target-aware methods with unrelated errors #88'syentry and this PR'staskentry).CrossFittedTransformer._fit_full: keepvalidate_task(self.task, type(self).__name__)together with CrossFittedTransformer converts DataFrames to ndarray; a wrapped Preprocessor treats every column as categorical #58'sX_arr = _as_2d(X).This change was previously part of #97, which is closed in favour of one pull request per issue.