Summary
When a target-aware selector finds fewer locations than min_count, _supplement tops the
set up with quantile candidates, unions the two, and then truncates with
sorted(all_locations)[:target_count]. Truncating a sorted union always removes the
largest values — which are frequently the data-driven locations the selector just
worked to find. The result is a location set biased toward the low end of the feature range.
Reproduction
import numpy as np
from pretab.core.selectors import CARTLocationSelector
sel = CARTLocationSelector()
x = np.linspace(0, 10, 500).reshape(-1, 1)
print("existing [9.5, 9.7] topped up to 5:", np.round(sel._supplement([9.5, 9.7], x, 5), 3))
existing [9.5, 9.7] topped up to 5: [1.667 3.333 5. 6.667 8.333]
Both of the real, tree-selected locations were discarded and replaced by pure quantiles. The
supplement step was supposed to add to them.
Visible through the public API as thresholds bunching at the bottom of the range. Here the
target depends on the feature only above x > 9, so CART finds few splits and the
supplement path triggers:
import numpy as np, pandas as pd
from pretab import Preprocessor
rng = np.random.default_rng(3)
xs = rng.uniform(0, 10, 500)
ys = np.where(xs > 9, 5.0, 0.0) + 0.1 * rng.normal(size=500)
pre = Preprocessor(numerical_method="ple", output_dim=7).fit(pd.DataFrame({"x": xs}), ys)
th = pre.column_transformer_.named_transformers_["num_x"].named_steps["ple"].thresholds_[0]
print("PLE thresholds (feature scaled to [-1, 1]):", np.round(th, 3))
PLE thresholds (feature scaled to [-1, 1]): [-0.911 -0.83 -0.759 -0.712 0.439 0.796]
Four of six thresholds sit inside the bottom 10% of the scaled range.
Expected
Supplementing preserves the locations the selector found and fills the remaining budget with
spread-out candidates — the added locations should complement the existing ones, not
displace them.
Actual
The union is truncated from the top, so high-valued selector locations are dropped in favour
of low quantiles, and the resulting set clusters at the low end.
Root cause
pretab/core/selectors.py:150-157:
def _supplement(self, existing, x, target_count):
if target_count - len(existing) <= 0:
return existing
quantile_candidates = quantile_knots(x, target_count)
all_locations = set(existing) | set(quantile_candidates.tolist())
return sorted(all_locations)[:target_count] # <- drops the largest values
quantile_knots(x, target_count) already returns target_count candidates, so the union has
target_count + len(existing) entries and the slice always discards exactly the
len(existing) largest — statistically, the selector's own locations whenever they sit above
the median.
Suggested fix
Keep existing unconditionally and only draw as many supplements as are actually missing:
def _supplement(self, existing, x, target_count):
missing = target_count - len(existing)
if missing <= 0:
return existing
candidates = [c for c in quantile_knots(x, target_count).tolist() if c not in set(existing)]
return sorted(set(existing) | set(candidates[:missing]))
If the union can still overshoot, down-sample by even spacing with
pretab.core.knots.select_knots rather than slicing a sorted list — the same primitive
trim_to_count already uses, which preserves coverage of the range instead of truncating one
end.
pretab/transformers/splines/mixins.py:119-130 (_clamp_interior_knots) has the same
shape of logic but already handles it correctly via select_knots, so that is a good
reference for the fix.
Regression test suggestion: assert that every element of existing is present in the output
of _supplement.
Impact
Any target-aware placement where the selector returns fewer than min_count locations —
common for weak or highly localized features. Reaches ple, the feature maps, and the
target-aware splines through both CARTLocationSelector and LightGBMLocationSelector.
Silent quality degradation, no error.
Environment
- pretab 0.1.0 (
main @ 51c3043)
- Python 3.11.15, numpy 2.4.6, pandas 2.3.3, scikit-learn 1.9.0, scipy 1.17.1
- macOS (darwin 25.5.0)
Summary
When a target-aware selector finds fewer locations than
min_count,_supplementtops theset up with quantile candidates, unions the two, and then truncates with
sorted(all_locations)[:target_count]. Truncating a sorted union always removes thelargest values — which are frequently the data-driven locations the selector just
worked to find. The result is a location set biased toward the low end of the feature range.
Reproduction
Both of the real, tree-selected locations were discarded and replaced by pure quantiles. The
supplement step was supposed to add to them.
Visible through the public API as thresholds bunching at the bottom of the range. Here the
target depends on the feature only above
x > 9, so CART finds few splits and thesupplement path triggers:
Four of six thresholds sit inside the bottom 10% of the scaled range.
Expected
Supplementing preserves the locations the selector found and fills the remaining budget with
spread-out candidates — the added locations should complement the existing ones, not
displace them.
Actual
The union is truncated from the top, so high-valued selector locations are dropped in favour
of low quantiles, and the resulting set clusters at the low end.
Root cause
pretab/core/selectors.py:150-157:quantile_knots(x, target_count)already returnstarget_countcandidates, so the union hastarget_count + len(existing)entries and the slice always discards exactly thelen(existing)largest — statistically, the selector's own locations whenever they sit abovethe median.
Suggested fix
Keep
existingunconditionally and only draw as many supplements as are actually missing:If the union can still overshoot, down-sample by even spacing with
pretab.core.knots.select_knotsrather than slicing a sorted list — the same primitivetrim_to_countalready uses, which preserves coverage of the range instead of truncating oneend.
pretab/transformers/splines/mixins.py:119-130(_clamp_interior_knots) has the sameshape of logic but already handles it correctly via
select_knots, so that is a goodreference for the fix.
Regression test suggestion: assert that every element of
existingis present in the outputof
_supplement.Impact
Any target-aware placement where the selector returns fewer than
min_countlocations —common for weak or highly localized features. Reaches
ple, the feature maps, and thetarget-aware splines through both
CARTLocationSelectorandLightGBMLocationSelector.Silent quality degradation, no error.
Environment
main@ 51c3043)