Talip7/scikit-learn-issues-multilabel huggingface dataset:
QuantileTransformer is incredibly slow

### Describe the workflow you want to enable

This is a feature request to improve performance of the QuantileTransformer. It takes ~60 minutes to fit, uses a huge amount of memory when transforming large non-sparse dataframes with 30M+ rows and 500 columns. It also does not support sample_weight.  Ideally it should be as fast as catboost's Pool quantize method, which does many of the same computations in a fraction of the time:
https://catboost.ai/docs/en/concepts/python-reference_pool_quantized


### Describe your proposed solution

See source code for https://catboost.ai/docs/en/concepts/python-reference_pool_quantized

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
Alright, after digging into this a bit the slowness  that comes from the transform_col function can be ameliorated:

```python
        # for forward transform, match the output distribution
        if not inverse:
            with np.errstate(invalid="ignore"):  # hide NaN comparison warnings
                if output_distribution == "normal":
                    X_col = stats.norm.ppf(X_col)
                    # find the value to clip the data to avoid mapping to
                    # infinity. Clip such that the inverse transform will be
                    # consistent
                    clip_min = stats.norm.ppf(BOUNDS_THRESHOLD - np.spacing(1))
                    clip_max = stats.norm.ppf(1 - (BOUNDS_THRESHOLD - np.spacing(1)))
                    X_col = np.clip(X_col, clip_min, clip_max)
                # else output distribution is uniform and the ppf is the
                # identity function so we let X_col unchanged

        return X_col

```

Since we are using loc=0, scale=1, one should be using `scipy.special.ndtri`  instead of `stats.norm.pff`. The latter carries the overhead of generalizing with non standard mean and variance and is much slower. In this case the output will be the same.

 @ogrisel @SauersML @Aishwarya0811   can you please assign someone to this ticket who actually cares about development and not a serial ticket-closer. This is a very small change for substantial performance gain. If you need me to make another post I will.

Labels: New Feature, Needs Triage
QuantileTransformer is incredibly slow

### Describe the workflow you want to enable

This is a feature request to improve performance of the QuantileTransformer. It takes ~60 minutes to fit, uses a huge amount of memory when transforming large non-sparse dataframes with 30M+ rows and 500 columns. It also does not support sample_weight.  Ideally it should be as fast as catboost's Pool quantize method, which does many of the same computations in a fraction of the time:
https://catboost.ai/docs/en/concepts/python-reference_pool_quantized


### Describe your proposed solution

See source code for https://catboost.ai/docs/en/concepts/python-reference_pool_quantized

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
@antipisa I have never looked at or used this code since I'm not a maintainer.

Here are some ideas you could try:

- Forward: stats.norm.ppf(X_col) (per column, per element), but could do ndtri(X_col) (inverse CDF of standard normal).

- Inverse: stats.norm.cdf(X_col) (per column, per element), but could do ndtr(X_col) (CDF of standard normal).

- Also clip bounds are recomputed each time with stats.norm.ppf, maybe `_clip_min, _clip_max` could be computed once and reused. Perhaps could be applied to references_ as well. (Lower impact.)

- It looks like for dense input, _transform loops columns and runs the normal CDF/PPF inside _transform_col for each column, but perhaps we could apply ndtri (forward) or ndtr (inverse) once to the whole dense matrix after the per-column interpolation.

- The percentile computation can use float64 even if input is float32, but this could have unintended consequences (haven't assessed).

Labels: New Feature, Needs Triage
QuantileTransformer is incredibly slow

### Describe the workflow you want to enable

This is a feature request to improve performance of the QuantileTransformer. It takes ~60 minutes to fit, uses a huge amount of memory when transforming large non-sparse dataframes with 30M+ rows and 500 columns. It also does not support sample_weight.  Ideally it should be as fast as catboost's Pool quantize method, which does many of the same computations in a fraction of the time:
https://catboost.ai/docs/en/concepts/python-reference_pool_quantized


### Describe your proposed solution

See source code for https://catboost.ai/docs/en/concepts/python-reference_pool_quantized

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
Feel free to open dedicated issues with speed up suggestions or memory improvements for either fit or transform with quick benchmark results for minimal code snippets that actually do the same thing as what `QuantileTransformer` does.

Also adding sample weight support would be welcome but its a completely different matter and should be done in dedicated issues. I am pretty sure we already have one for this but i am writing this comment on my phone so it's not easy for me to find it.

Labels: New Feature, Needs Triage
QuantileTransformer is incredibly slow

### Describe the workflow you want to enable

This is a feature request to improve performance of the QuantileTransformer. It takes ~60 minutes to fit, uses a huge amount of memory when transforming large non-sparse dataframes with 30M+ rows and 500 columns. It also does not support sample_weight.  Ideally it should be as fast as catboost's Pool quantize method, which does many of the same computations in a fraction of the time:
https://catboost.ai/docs/en/concepts/python-reference_pool_quantized


### Describe your proposed solution

See source code for https://catboost.ai/docs/en/concepts/python-reference_pool_quantized

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
Also it would be good to analyze profiling results of either fit or transform to better understand performance bottlenecks before discussing possible solutions.

Labels: New Feature, Needs Triage
QuantileTransformer is incredibly slow

### Describe the workflow you want to enable

This is a feature request to improve performance of the QuantileTransformer. It takes ~60 minutes to fit, uses a huge amount of memory when transforming large non-sparse dataframes with 30M+ rows and 500 columns. It also does not support sample_weight.  Ideally it should be as fast as catboost's Pool quantize method, which does many of the same computations in a fraction of the time:
https://catboost.ai/docs/en/concepts/python-reference_pool_quantized


### Describe your proposed solution

See source code for https://catboost.ai/docs/en/concepts/python-reference_pool_quantized

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
Furthermore and most importantly please respect the time and efforts put by project maintainers in reviewing the issues you submit. Given the publicly accessible track record of Christian, statements such as 'who actually cares about development' are just insulting. If you want to continue collaborating with us or other open source projects in general i would strongly suggest revising the tone of your future interactions to be more respectful and constructive.

Labels: New Feature, Needs Triage
Add `covariance_estimator` to `QuadraticDiscriminantAnalysis`?

### Describe the workflow you want to enable

`LinearDiscriminantAnalysis` has an optional `covariance_estimator` parameter, while the similar `QuadraticDiscriminantAnalysis` does not. QDA is even more sensitive than LDA to covariance estimation.

Would it be desirable to add the `covariance_estimator` parameter to `QuadraticDiscriminantAnalysis`? 

### Describe your proposed solution

I can try to implement this. I would look at how it is done in `LinearDiscriminantAnalysis`, and just copy that implementation into `QuadraticDiscriminantAnalysis`.

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
“Hi @dherrera1911, I’d be interested in implementing this feature. I’ve reviewed the QDA code and can explore how to integrate a covariance_estimator parameter. Would you like me to draft a PR?”

Labels: New Feature
Add `covariance_estimator` to `QuadraticDiscriminantAnalysis`?

### Describe the workflow you want to enable

`LinearDiscriminantAnalysis` has an optional `covariance_estimator` parameter, while the similar `QuadraticDiscriminantAnalysis` does not. QDA is even more sensitive than LDA to covariance estimation.

Would it be desirable to add the `covariance_estimator` parameter to `QuadraticDiscriminantAnalysis`? 

### Describe your proposed solution

I can try to implement this. I would look at how it is done in `LinearDiscriminantAnalysis`, and just copy that implementation into `QuadraticDiscriminantAnalysis`.

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
@dherrera1911 thanks for your issue. Your proposal sounds reasonable. Feel free to go-ahead with a PR.

@Angel-Mathew007 explicitly expressed interest in contributing this in the description. Your profile name and your use of quotes in your comment and the fact that you ignore the expressed intentions of the original poster all seem to indicate that your github account is automatically operated. Please review our contribution policy: https://scikit-learn.org/dev/developers/contributing.html#automated-contributions-policy . In case of repeated offense, this account will be blocked.

Labels: New Feature
Add `covariance_estimator` to `QuadraticDiscriminantAnalysis`?

### Describe the workflow you want to enable

`LinearDiscriminantAnalysis` has an optional `covariance_estimator` parameter, while the similar `QuadraticDiscriminantAnalysis` does not. QDA is even more sensitive than LDA to covariance estimation.

Would it be desirable to add the `covariance_estimator` parameter to `QuadraticDiscriminantAnalysis`? 

### Describe your proposed solution

I can try to implement this. I would look at how it is done in `LinearDiscriminantAnalysis`, and just copy that implementation into `QuadraticDiscriminantAnalysis`.

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
@Angel-Mathew007 please answer this message to acknowledge the previous comment. If you fail to do so, I will block your account in the coming days.

Labels: New Feature
Add `covariance_estimator` to `QuadraticDiscriminantAnalysis`?

### Describe the workflow you want to enable

`LinearDiscriminantAnalysis` has an optional `covariance_estimator` parameter, while the similar `QuadraticDiscriminantAnalysis` does not. QDA is even more sensitive than LDA to covariance estimation.

Would it be desirable to add the `covariance_estimator` parameter to `QuadraticDiscriminantAnalysis`? 

### Describe your proposed solution

I can try to implement this. I would look at how it is done in `LinearDiscriminantAnalysis`, and just copy that implementation into `QuadraticDiscriminantAnalysis`.

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
Thanks @ogrisel, I went ahead and submitted the PR.

Looking forward to get feedback in the review.

Labels: New Feature
Add `covariance_estimator` to `QuadraticDiscriminantAnalysis`?

### Describe the workflow you want to enable

`LinearDiscriminantAnalysis` has an optional `covariance_estimator` parameter, while the similar `QuadraticDiscriminantAnalysis` does not. QDA is even more sensitive than LDA to covariance estimation.

Would it be desirable to add the `covariance_estimator` parameter to `QuadraticDiscriminantAnalysis`? 

### Describe your proposed solution

I can try to implement this. I would look at how it is done in `LinearDiscriminantAnalysis`, and just copy that implementation into `QuadraticDiscriminantAnalysis`.

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
Hi @ogrisel, is there something I can do to help the maintainers start the review of the PR above?

I know you are all very busy and don't want to bother, but this seems like a simple yet relevant addition that I hope doesn't fall through the cracks.

Labels: New Feature
Add `covariance_estimator` to `QuadraticDiscriminantAnalysis`?

### Describe the workflow you want to enable

`LinearDiscriminantAnalysis` has an optional `covariance_estimator` parameter, while the similar `QuadraticDiscriminantAnalysis` does not. QDA is even more sensitive than LDA to covariance estimation.

Would it be desirable to add the `covariance_estimator` parameter to `QuadraticDiscriminantAnalysis`? 

### Describe your proposed solution

I can try to implement this. I would look at how it is done in `LinearDiscriminantAnalysis`, and just copy that implementation into `QuadraticDiscriminantAnalysis`.

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
Completed by #32108

Labels: New Feature
⚠️ CI failed on Wheel builder (last failure: Aug 08, 2025) ⚠️

**CI failed on [Wheel builder](https://github.com/scikit-learn/scikit-learn/actions/runs/16821604494)** (Aug 08, 2025)

COMMENT:
## CI is no longer failing! ✅

[Successful run](https://github.com/scikit-learn/scikit-learn/actions/runs/16821604494) on Aug 08, 2025

Labels: Needs Triage
TunedThreasholdClassiffierCV not understanding `func(y_pred, y_true, ...)` as a valid `scoring`

This code

```py
from sklearn.model_selection import TunedThresholdClassifierCV
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
import sklearn
import numpy as np

sklearn.set_config(enable_metadata_routing=True)

def my_metric(y_true, y_pred, sample_weight=None):
    assert sample_weight is not None
    return np.mean(y_pred)

X, y = make_classification(random_state=0)
sample_weight = np.random.rand(len(y))

est = TunedThresholdClassifierCV(LogisticRegression(), cv=2, scoring=my_metric)
est.fit(X, y, sample_weight=sample_weight)
```

gives this:

```py
Traceback (most recent call last):
  File "/tmp/2.py", line 17, in <module>
    est.fit(X, y, sample_weight=sample_weight)
  File "/path/to/scikit-learn/sklearn/base.py", line 1366, in wrapper
    return fit_method(estimator, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 129, in fit
    self._fit(X, y, **params)
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 742, in _fit
    routed_params = process_routing(self, "fit", **params)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/utils/_metadata_requests.py", line 1636, in process_routing
    request_routing = get_routing_for_object(_obj)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/utils/_metadata_requests.py", line 1197, in get_routing_for_object
    return deepcopy(obj.get_metadata_routing())
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 871, in get_metadata_routing
    scorer=self._get_curve_scorer(),
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 880, in _get_curve_scorer
    curve_scorer = _CurveScorer.from_scorer(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/metrics/_scorer.py", line 1108, in from_scorer
    score_func=scorer._score_func,
               ^^^^^^^^^^^^^^^^^^
AttributeError: 'function' object has no attribute '_score_func'
```

cc @glemaitre

COMMENT:
Hi! I'd like to work on this issue as a first-time contributor. I've reproduced the bug and would like to explore a fix. Is anyone currently working on this?

Also, I noticed that the error occurs when using a plain function instead of a scorer object. One potential fix could be wrapping the function with `make_scorer` internally or improving the error message.

Would love your thoughts before I begin. Thanks!

Labels: Bug
TunedThreasholdClassiffierCV not understanding `func(y_pred, y_true, ...)` as a valid `scoring`

This code

```py
from sklearn.model_selection import TunedThresholdClassifierCV
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
import sklearn
import numpy as np

sklearn.set_config(enable_metadata_routing=True)

def my_metric(y_true, y_pred, sample_weight=None):
    assert sample_weight is not None
    return np.mean(y_pred)

X, y = make_classification(random_state=0)
sample_weight = np.random.rand(len(y))

est = TunedThresholdClassifierCV(LogisticRegression(), cv=2, scoring=my_metric)
est.fit(X, y, sample_weight=sample_weight)
```

gives this:

```py
Traceback (most recent call last):
  File "/tmp/2.py", line 17, in <module>
    est.fit(X, y, sample_weight=sample_weight)
  File "/path/to/scikit-learn/sklearn/base.py", line 1366, in wrapper
    return fit_method(estimator, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 129, in fit
    self._fit(X, y, **params)
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 742, in _fit
    routed_params = process_routing(self, "fit", **params)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/utils/_metadata_requests.py", line 1636, in process_routing
    request_routing = get_routing_for_object(_obj)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/utils/_metadata_requests.py", line 1197, in get_routing_for_object
    return deepcopy(obj.get_metadata_routing())
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 871, in get_metadata_routing
    scorer=self._get_curve_scorer(),
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 880, in _get_curve_scorer
    curve_scorer = _CurveScorer.from_scorer(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/metrics/_scorer.py", line 1108, in from_scorer
    score_func=scorer._score_func,
               ^^^^^^^^^^^^^^^^^^
AttributeError: 'function' object has no attribute '_score_func'
```

cc @glemaitre

COMMENT:
Actually, it is not a bug because it is inline with the documentation. We accept callable that in the form of a scorer `scorer(estimator, X, y)`.

So this would not be a bug but rather an enhancement. But I don't necessarily know how to distinguish a scorer callable from a score function callable with imposing constraints on the variable names.

Labels: Bug
TunedThreasholdClassiffierCV not understanding `func(y_pred, y_true, ...)` as a valid `scoring`

This code

```py
from sklearn.model_selection import TunedThresholdClassifierCV
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
import sklearn
import numpy as np

sklearn.set_config(enable_metadata_routing=True)

def my_metric(y_true, y_pred, sample_weight=None):
    assert sample_weight is not None
    return np.mean(y_pred)

X, y = make_classification(random_state=0)
sample_weight = np.random.rand(len(y))

est = TunedThresholdClassifierCV(LogisticRegression(), cv=2, scoring=my_metric)
est.fit(X, y, sample_weight=sample_weight)
```

gives this:

```py
Traceback (most recent call last):
  File "/tmp/2.py", line 17, in <module>
    est.fit(X, y, sample_weight=sample_weight)
  File "/path/to/scikit-learn/sklearn/base.py", line 1366, in wrapper
    return fit_method(estimator, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 129, in fit
    self._fit(X, y, **params)
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 742, in _fit
    routed_params = process_routing(self, "fit", **params)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/utils/_metadata_requests.py", line 1636, in process_routing
    request_routing = get_routing_for_object(_obj)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/utils/_metadata_requests.py", line 1197, in get_routing_for_object
    return deepcopy(obj.get_metadata_routing())
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 871, in get_metadata_routing
    scorer=self._get_curve_scorer(),
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/model_selection/_classification_threshold.py", line 880, in _get_curve_scorer
    curve_scorer = _CurveScorer.from_scorer(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/scikit-learn/sklearn/metrics/_scorer.py", line 1108, in from_scorer
    score_func=scorer._score_func,
               ^^^^^^^^^^^^^^^^^^
AttributeError: 'function' object has no attribute '_score_func'
```

cc @glemaitre

COMMENT:
We need to have a single place (`check_scoring`) where all validation happens, and clean up the code in all other places. I think it'd be nice if `check_scoring` could:

- return a scorer if input is `f(y_pred, y_true)`
- make sure the output is always a `BaseScorer`
- handle multi metric scoring as well

Labels: Bug