Quantile transformer sample weight - #32761
Conversation
…rcentile, add XFAIL for sparse_data
|
Hmmmm I am running with an issue that seems similar to here: actions/runner#449 |
|
the integration of |
antoinebaker
left a comment
There was a problem hiding this comment.
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.
| 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) |
There was a problem hiding this comment.
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").
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
@antoinebaker yes we could use effective_sample_size this was at some point tried in the #30751? Let me update.
There was a problem hiding this comment.
check if it makes sense now?
| 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) | ||
| ) |
There was a problem hiding this comment.
Here is the warning message in question in #32761 (comment)
|
@antoinebaker let me know what you think? |
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. 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: 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). |
| n_quantiles_ : int | ||
| The actual number of quantiles used to discretize the cumulative | ||
| distribution function. | ||
|
|
There was a problem hiding this comment.
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)
| 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 |
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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
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:
|
|
Thanks @antoinebaker and @jeremiedbb have updated seems fine now. |
There was a problem hiding this comment.
LGTM @snath-xoc :)
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. |
| self.quantiles_ = _weighted_percentile( | ||
| X, | ||
| sample_weight=sample_weight, | ||
| percentile_rank=references, | ||
| average=True, | ||
| ) | ||
| self.quantiles_ = np.asarray(self.quantiles_).T |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Good catch, turns out the NaN handling is not done correctly, I added in a fix for it, let me know what you think.
| self.quantiles_.append( | ||
| np.nanquantile( | ||
| column_data, references / 100, method="averaged_inverted_cdf" | ||
| ) | ||
| ) | ||
| self.quantiles_ = np.transpose(self.quantiles_) |
There was a problem hiding this comment.
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.
|
@ogrisel @jeremiedbb and @antoinebaker this should be ready now? |
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.