Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Quantile transformer sample weight - #32761

Open
snath-xoc wants to merge 77 commits into
scikit-learn:mainfrom
snath-xoc:quantile-transformer-sample-weight
Open

Quantile transformer sample weight#32761
snath-xoc wants to merge 77 commits into
scikit-learn:mainfrom
snath-xoc:quantile-transformer-sample-weight

Conversation

@snath-xoc

@snath-xoc snath-xoc commented Nov 21, 2025

Copy link
Copy Markdown
Contributor

Reference Issues/PRs

Follow up on previously closed PR #31147 and issue #30707

What does this implement/fix? Explain your changes.

Adds sample weights within the QuantileTransformer under preprocessing using the _weighted_percentile function.

@github-actions

github-actions Bot commented Nov 21, 2025

Copy link
Copy Markdown

✔️ Linting Passed

All linting checks passed. Your pull request is in excellent shape! ☀️

Generated for commit: fa29103. Link to the linter CI: here

@snath-xoc

Copy link
Copy Markdown
Contributor Author

@ogrisel and @kaekkr

@snath-xoc

Copy link
Copy Markdown
Contributor Author

Hmmmm I am running with an issue that seems similar to here: actions/runner#449

@Mohataseem89

Mohataseem89 commented Dec 8, 2025

Copy link
Copy Markdown

the integration of _weighted_percentile and the updates based on earlier review feedback look like a solid step toward bringing sample-weight support to QuantileTransformer.
good to see the switch to the unified _weighted_percentile logic — that aligns with recent changes in utils.stats.
as @ogrisel mentioned earlier, using percentile ranks correctly (per feature and per reference value) will be key for correctness.

@adrinjalali adrinjalali added this to Labs Dec 11, 2025
@github-project-automation github-project-automation Bot moved this to Todo in Labs Dec 11, 2025
@adrinjalali adrinjalali moved this from Todo to In progress in Labs Dec 11, 2025

@antoinebaker antoinebaker left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @snath-xoc for the PR ! The logic seems fine to me.

I made a few suggestions regarding the docstrings / warning message etc, but I don't have a strong opinion. I let you make your own mind :)

Do not hesitate to ping me when it's ready for another review.

Comment thread doc/whats_new/upcoming_changes/sklearn.preprocessing/32761.enhancement.rst Outdated
Comment thread doc/whats_new/upcoming_changes/sklearn.preprocessing/32761.enhancement.rst Outdated
Comment thread sklearn/preprocessing/_data.py Outdated
Comment thread sklearn/preprocessing/_data.py Outdated
Comment on lines +2915 to +2919
if sample_weight is None:
n_samples = X.shape[0]
else:
sample_weight = _check_sample_weight(sample_weight, X, dtype=X.dtype)
n_samples = np.sum(sample_weight)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If n_samples stands for the sum of sample weights, we should perhaps mention it in the docstring and warning messages ? But on the other hand it may be too verbose (at each time saying "the number of samples or the total sum of weights if provided").

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a middle ground would be to replace total number of samples -> effective number of samples in the warning messages and the n_quantiles docstring ? Maybe we should replace n_samples by a better name ?

@snath-xoc snath-xoc Feb 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antoinebaker yes we could use effective_sample_size this was at some point tried in the #30751? Let me update.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if it makes sense now?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep I like the name !

Comment thread sklearn/preprocessing/_data.py Outdated
Comment thread sklearn/preprocessing/_data.py Outdated
Comment thread sklearn/preprocessing/_data.py Outdated
Comment on lines 2921 to 2926
if self.n_quantiles > n_samples:
warnings.warn(
"n_quantiles (%s) is greater than the total number "
"of samples (%s). n_quantiles is set to "
"n_samples." % (self.n_quantiles, n_samples)
)

@antoinebaker antoinebaker Feb 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the warning message in question in #32761 (comment)

@snath-xoc

Copy link
Copy Markdown
Contributor Author

@antoinebaker let me know what you think?

@jeremiedbb

Copy link
Copy Markdown
Member

The n_unique solution seemed cleaner in my mind and is more inline with KBinsDiscretizer but then with all the other raised errors it seems to not be so good.

the n_unique solution is a good idea but it should be done before computing the quantiles otherwise we waste time computing too many quantiles. n_quantiles could instead be determined from the number of unique rows in X. However it also has a cost and should not be done of large datasets. And is probably overkill for the issue we're trying to solve.

So in the end I believe that just dropping the cap on n_quantiles if fine. It adds some overhead on small datasets because we compute more quantiles than necessary, but I think it's acceptable. Here's a quick benchmark:

  config                no-cap n_q_  capped n_q_   no-cap ms   capped ms    diff ms   slowdown
----------------------------------------------------------------------------------------------
  n=10,   f=5                  1000           10       0.603       0.493      0.110      1.22x
  n=10,   f=50                 1000           10       2.766       1.805      0.961      1.53x
  n=50,   f=5                  1000           50       0.626       0.519      0.107      1.21x
  n=50,   f=50                 1000           50       3.039       2.095      0.945      1.45x
  n=100,  f=5                  1000          100       0.668       0.575      0.093      1.16x
  n=100,  f=50                 1000          100       3.406       2.580      0.826      1.32x
  n=500,  f=10                 1000          500       2.652       2.553      0.099      1.04x
  n=500,  f=100                1000          500      22.863      21.794      1.069      1.05x
  n=2000, f=10                 1000         1000      22.814      22.815     -0.001      1.00x
  n=2000, f=100                1000         1000     222.813     222.601      0.212      1.00x
  n=5000, f=50                 1000         1000      26.866      27.036     -0.170      0.99x

The slowdown is only significant for tiny datasets (up to 50%), but the purpose of QuantileTransformer is to deal with large datasets. I don't think we worry too much about performance for datasets that small for which QuantileTransformer will never be used in practice (besides testing).

Comment on lines -2736 to -2739
n_quantiles_ : int
The actual number of quantiles used to discretize the cumulative
distribution function.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't just remove a public attribute like that. Either we keep it and it's always equal to n_quantiles, or we go through a deprecation cycle. (note that to separate concern we can keep it for now in this PR and decide to deprecate later)

Comment thread sklearn/preprocessing/_data.py Outdated
Comment on lines +2700 to +2704
landmarks used to discretize the cumulative distribution function. If
n_quantiles is larger than the effective sample size (sum of sample
weights if provided, total number of samples otherwise), n_quantiles is
set to the effective sample size as a larger number of quantiles does
not give a better approximation of the cumulative distribution function

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be removed if we no longer cap the number of quantiles, and probably add a ..versionchanged directive to explain the change.

raise NotImplementedError(
"sample_weight is not supported for sparse input."
)
self.n_quantiles_ = max(1, min(self.n_quantiles, n_samples))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following on https://github.com/scikit-learn/scikit-learn/pull/32761/changes#r3673274161 we should (for now) keep the attribute:

self.n_quantiles_ = self.n_quantiles

@antoinebaker

antoinebaker commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@antoinebaker let me know what you think?

Sorry for the late answer :( Just removing the criterion seems a good solution, I agree with @jeremiedbb that the performance drawback is not a serious one:

The slowdown is only significant for tiny datasets (up to 50%), but the purpose of QuantileTransformer is to deal with large datasets. I don't think we worry too much about performance for datasets that small for which QuantileTransformer will never be used in practice (besides testing).

@snath-xoc

Copy link
Copy Markdown
Contributor Author

Thanks @antoinebaker and @jeremiedbb have updated seems fine now.

@antoinebaker antoinebaker left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM @snath-xoc :)

EDIT: there is a doctest failure to fix in preprocessing.rst

@jeremiedbb

Copy link
Copy Markdown
Member

EDIT: there is a doctest failure to fix in preprocessing.rst

We should set n_quantiles=150 in this doc example now that it's not capped.

Comment on lines +2841 to +2847
self.quantiles_ = _weighted_percentile(
X,
sample_weight=sample_weight,
percentile_rank=references,
average=True,
)
self.quantiles_ = np.asarray(self.quantiles_).T

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some sort of inconsistency here: input with NaNs that have non-zero weights are not discarded while in the non-weighted case inputs with non-zero weights are discarded.

Let's add a test for that

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, turns out the NaN handling is not done correctly, I added in a fix for it, let me know what you think.

Comment on lines +2893 to 2898
self.quantiles_.append(
np.nanquantile(
column_data, references / 100, method="averaged_inverted_cdf"
)
)
self.quantiles_ = np.transpose(self.quantiles_)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the change of method is a change of behavior. We should document it in the changelog.

Also, now we're doing

references = self.references_ * 100
np.nanquantile(column_data, references / 100, method="averaged_inverted_cdf")

Let's just use self.references_ directly.

@github-actions github-actions Bot added the CI:Linter failure The linter CI is failing on this PR label Sep 7, 2026
@ogrisel ogrisel moved this from In progress to PR waiting for reviews in Labs Sep 7, 2026
Comment thread sklearn/preprocessing/tests/test_data.py
@github-actions github-actions Bot removed the CI:Linter failure The linter CI is failing on this PR label Sep 10, 2026
@snath-xoc

Copy link
Copy Markdown
Contributor Author

@ogrisel @jeremiedbb and @antoinebaker this should be ready now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants