Talip7/scikit-learn-issues-multilabel huggingface dataset:
Improve TargetEncoder predict time for single rows and many categories

As reported [here](https://tiago.rio.br/work/willbank/account/patching-scikit-learn-improve-api-performance/), `TargetEncoder.transform` is optimized for large `n_samples`. But in deployment mode, it might be single rows that matter. Combined with high cardinality of the categories, `transform` can be slow, but has room for improvement.

COMMENT:
hello @lorentzenchr @glemaitre @lesteve - this slowdown hits my day-to-day workflows at work  (lots of single-row / tiny-batch transforms with high-cardinality categories). Thus it affects our production workflows and, by extension, 100s of thousands of users. If it’s okay with you, I’d like to take over this issue and see it through.  I took a look over the weekend and opened a PR (#32368) - It has already cleared the CI pipeline, just waiting for a review. 

For transparency: I’ve been searching across the repo for a fix or prior discussion since this issue was opened in March and couldn’t find an existing solution or PR addressing it. If I missed something, please point me to it and I’ll align with that direction.

**Exact logic in my PR:**

* **Adaptive fast path:**
  In `TargetEncoder.transform`, if `n_samples <= small_batch_threshold` (configurable; defaults to a small value) we switch to a lookup-based path; otherwise we keep the existing vectorized/groupby path.

* **Precomputed stats (from `fit`):**
  For each encoded feature I store:

  1. an array `stat_per_code` aligned to the training categories’ integer codes,
  2. the global prior (for unknowns), and
  3. any smoothing parameters already computed in `fit`.

* **Encoding via integer codes (no groupby in small batches):**

  * Convert input column to the training categories’ **ordinal codes** captured in `fit`.
  * Use `np.take(stat_per_code, codes, mode='wrap')` (or equivalent) to gather values in O(n_samples).
  * Replace codes of `-1` (unknown) with the learned prior / `handle_unknown` policy.
  * Smoothing uses the precomputed, per-category values from `fit` (no per-call recomputation).

* **Allocation minimization:**
  Preallocate the output `ndarray` for all encoded features and write in-place; wrap as a `DataFrame` at the end to preserve column names/index.

* **Behavioral parity:**
  Outputs match the current implementation for known/unknown categories; the change is purely about the execution path for tiny batches. Large batches fall back to the original path to keep current performance characteristics.

If this direction looks good, I’m happy to tune the threshold, add/adjust tests, and update docs. Please let me know if it’s okay that I takeover this issue and continue driving the PR. Thanks so much for your time!

Labels: Performance, module:preprocessing
Potential Problem in the Computation of Adjusted Mutual Info Score

### Describe the bug

It seems to me that for clusters of size 2 and 4, the AMI yields unexpected results of 0 instead of 1, if all items belong to different clusters. 



### Steps/Code to Reproduce

Sample Code to Reproduce:

```python
>>> from sklearn.metrics.cluster import adjusted_mutual_info_score
>>> print(adjusted_mutual_info_score([1, 2], [3, 4])
0.0
>>> print(adjusted_mutual_info_score([1, 2, 3, 4], [5, 6, 7, 8])
0.0
>>> print(adjusted_mutual_info_score([1, 2, 3, 4, 5], [6, 7, 8, 9, 10])
1.0
```

### Expected Results

As the clusters are identical in all cases, I'd expect the result to be 1.0 in all cases. This happens with version 1.6.1.

### Actual Results

So we have the strange behavior that the code outputs for lists containing different labels with 2 items and with 4 items the value 0, while we deal with identical partitions. I tested until 1000 items, it only occurs with 2 and 4.

### Versions

```shell
System:
    python: 3.13.1 (main, Dec  4 2024, 18:05:56) [GCC 14.2.1 20240910]
executable: /home/mattis/.envs/lexi/bin/python
   machine: Linux-6.12.10-arch1-1-x86_64-with-glibc2.41

Python dependencies:
      sklearn: 1.6.1
          pip: 24.3.1
   setuptools: 75.8.0
        numpy: 2.2.2
        scipy: 1.15.2
       Cython: None
       pandas: None
   matplotlib: None
       joblib: 1.4.2
threadpoolctl: 3.5.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 20
         prefix: libscipy_openblas
       filepath: /home/mattis/.envs/lexi/lib/python3.13/site-packages/numpy.libs/libscipy_openblas64_-6bb31eeb.so
        version: 0.3.28
threading_layer: pthreads
   architecture: Haswell

       user_api: blas
   internal_api: openblas
    num_threads: 20
         prefix: libscipy_openblas
       filepath: /home/mattis/.envs/lexi/lib/python3.13/site-packages/scipy.libs/libscipy_openblas-68440149.so
        version: 0.3.28
threading_layer: pthreads
   architecture: Haswell

       user_api: openmp
   internal_api: openmp
    num_threads: 20
         prefix: libgomp
       filepath: /home/mattis/.envs/lexi/lib/python3.13/site-packages/scikit_learn.libs/libgomp-a34b3233.so.1.0.0
        version: None
```

COMMENT:
Weird, it seems like a real bug, although probably not that important in practice? I guess someone needs to investigate in more details?

Labels: Bug
Potential Problem in the Computation of Adjusted Mutual Info Score

### Describe the bug

It seems to me that for clusters of size 2 and 4, the AMI yields unexpected results of 0 instead of 1, if all items belong to different clusters. 



### Steps/Code to Reproduce

Sample Code to Reproduce:

```python
>>> from sklearn.metrics.cluster import adjusted_mutual_info_score
>>> print(adjusted_mutual_info_score([1, 2], [3, 4])
0.0
>>> print(adjusted_mutual_info_score([1, 2, 3, 4], [5, 6, 7, 8])
0.0
>>> print(adjusted_mutual_info_score([1, 2, 3, 4, 5], [6, 7, 8, 9, 10])
1.0
```

### Expected Results

As the clusters are identical in all cases, I'd expect the result to be 1.0 in all cases. This happens with version 1.6.1.

### Actual Results

So we have the strange behavior that the code outputs for lists containing different labels with 2 items and with 4 items the value 0, while we deal with identical partitions. I tested until 1000 items, it only occurs with 2 and 4.

### Versions

```shell
System:
    python: 3.13.1 (main, Dec  4 2024, 18:05:56) [GCC 14.2.1 20240910]
executable: /home/mattis/.envs/lexi/bin/python
   machine: Linux-6.12.10-arch1-1-x86_64-with-glibc2.41

Python dependencies:
      sklearn: 1.6.1
          pip: 24.3.1
   setuptools: 75.8.0
        numpy: 2.2.2
        scipy: 1.15.2
       Cython: None
       pandas: None
   matplotlib: None
       joblib: 1.4.2
threadpoolctl: 3.5.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 20
         prefix: libscipy_openblas
       filepath: /home/mattis/.envs/lexi/lib/python3.13/site-packages/numpy.libs/libscipy_openblas64_-6bb31eeb.so
        version: 0.3.28
threading_layer: pthreads
   architecture: Haswell

       user_api: blas
   internal_api: openblas
    num_threads: 20
         prefix: libscipy_openblas
       filepath: /home/mattis/.envs/lexi/lib/python3.13/site-packages/scipy.libs/libscipy_openblas-68440149.so
        version: 0.3.28
threading_layer: pthreads
   architecture: Haswell

       user_api: openmp
   internal_api: openmp
    num_threads: 20
         prefix: libgomp
       filepath: /home/mattis/.envs/lexi/lib/python3.13/site-packages/scikit_learn.libs/libgomp-a34b3233.so.1.0.0
        version: None
```

COMMENT:
Yes, it occurs only if you stubbornly try to determine how these methods react on different clusters. That's what I did. But it leaves some general suspicion to the method, right? Specifically the fact that it occurs in only those two cases (as far as I have investigated it).

Labels: Bug
Potential Problem in the Computation of Adjusted Mutual Info Score

### Describe the bug

It seems to me that for clusters of size 2 and 4, the AMI yields unexpected results of 0 instead of 1, if all items belong to different clusters. 



### Steps/Code to Reproduce

Sample Code to Reproduce:

```python
>>> from sklearn.metrics.cluster import adjusted_mutual_info_score
>>> print(adjusted_mutual_info_score([1, 2], [3, 4])
0.0
>>> print(adjusted_mutual_info_score([1, 2, 3, 4], [5, 6, 7, 8])
0.0
>>> print(adjusted_mutual_info_score([1, 2, 3, 4, 5], [6, 7, 8, 9, 10])
1.0
```

### Expected Results

As the clusters are identical in all cases, I'd expect the result to be 1.0 in all cases. This happens with version 1.6.1.

### Actual Results

So we have the strange behavior that the code outputs for lists containing different labels with 2 items and with 4 items the value 0, while we deal with identical partitions. I tested until 1000 items, it only occurs with 2 and 4.

### Versions

```shell
System:
    python: 3.13.1 (main, Dec  4 2024, 18:05:56) [GCC 14.2.1 20240910]
executable: /home/mattis/.envs/lexi/bin/python
   machine: Linux-6.12.10-arch1-1-x86_64-with-glibc2.41

Python dependencies:
      sklearn: 1.6.1
          pip: 24.3.1
   setuptools: 75.8.0
        numpy: 2.2.2
        scipy: 1.15.2
       Cython: None
       pandas: None
   matplotlib: None
       joblib: 1.4.2
threadpoolctl: 3.5.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 20
         prefix: libscipy_openblas
       filepath: /home/mattis/.envs/lexi/lib/python3.13/site-packages/numpy.libs/libscipy_openblas64_-6bb31eeb.so
        version: 0.3.28
threading_layer: pthreads
   architecture: Haswell

       user_api: blas
   internal_api: openblas
    num_threads: 20
         prefix: libscipy_openblas
       filepath: /home/mattis/.envs/lexi/lib/python3.13/site-packages/scipy.libs/libscipy_openblas-68440149.so
        version: 0.3.28
threading_layer: pthreads
   architecture: Haswell

       user_api: openmp
   internal_api: openmp
    num_threads: 20
         prefix: libgomp
       filepath: /home/mattis/.envs/lexi/lib/python3.13/site-packages/scikit_learn.libs/libgomp-a34b3233.so.1.0.0
        version: None
```

COMMENT:
Problem lies in `expected_mutual_information` calculation. When `n_samples` == `n_classes` and `n_classes` == 2 or 4, `expected_mutual_information` it is equal to `mutual_info_score`, while in all other cases it is slightly bigger. In the end, when you have `adjusted_mutual_info_score([0, 1], [0, 1])` you will get `0 / 2.220446049250313e-16` which is 0, but in the `adjusted_mutual_info_score([1, 2, 3], [1, 2, 3])` you will get `-2.220446049250313e-16 / -2.220446049250313e-16` which is 1.
Maybe somebody can try fix this numerical instability in `_expected_mutual_info_fast.pyx`? 
Or the other way could be to do the same clip operation as. in
https://github.com/scikit-learn/scikit-learn/blob/d2b75f6ae11543330a6572a566baa7bd32e0490f/sklearn/metrics/cluster/_supervised.py#L1050-L1053
on numerator also AKA `(mi - emi)`?

edit: grammar

Labels: Bug
RANSAC randomly raises UndefinedMetricWarning

RANSAC randomly raises undefined R2 warning for non-default `min_samples` (say 0.1) even if X is sufficiently large.

```
UndefinedMetricWarning: R^2 score is not well-defined with less than two samples. warnings.warn(msg, UndefinedMetricWarning)
```

It seems after applying the inlier mask the number of samples can fall below 2. Adding a check before calling `score` may be reasonable here?

https://github.com/scikit-learn/scikit-learn/blob/d0ee195cdc1e321ec1d094283aaa30fe061d9572/sklearn/linear_model/_ransac.py#L520-L540

Update: changing the initial `n_inliers_best` to 2 fixes for linear regressor.
https://github.com/scikit-learn/scikit-learn/blob/d0ee195cdc1e321ec1d094283aaa30fe061d9572/sklearn/linear_model/_ransac.py#L452-L452

COMMENT:
Please provide [self-contained example code](https://scikit-learn.org/dev/developers/minimal_reproducer.html), including imports and data (if possible), so that other contributors can just run it and reproduce your issue. Ideally your example code should be minimal.

Labels: Needs Reproducible Code
RANSAC randomly raises UndefinedMetricWarning

RANSAC randomly raises undefined R2 warning for non-default `min_samples` (say 0.1) even if X is sufficiently large.

```
UndefinedMetricWarning: R^2 score is not well-defined with less than two samples. warnings.warn(msg, UndefinedMetricWarning)
```

It seems after applying the inlier mask the number of samples can fall below 2. Adding a check before calling `score` may be reasonable here?

https://github.com/scikit-learn/scikit-learn/blob/d0ee195cdc1e321ec1d094283aaa30fe061d9572/sklearn/linear_model/_ransac.py#L520-L540

Update: changing the initial `n_inliers_best` to 2 fixes for linear regressor.
https://github.com/scikit-learn/scikit-learn/blob/d0ee195cdc1e321ec1d094283aaa30fe061d9572/sklearn/linear_model/_ransac.py#L452-L452

COMMENT:
Closing due to lack of activity from the OP. Happy to reopen if you get to work on this further.

Labels: Needs Reproducible Code
Pipeline score asks to explicitly request sample_weight

### Describe the bug

When using `Pipeline` with metadata routing enabled,  an error is thrown unless we explicitly request `sample_weight` for the `score` method (see example below). But `Pipeline` is just a router (for both the `fit` and `score` methods) and not a consumer of `sample_weight`, so in principle it should not require `sample_weight` to be explicitly requested.

### Steps/Code to Reproduce

```python
import numpy as np
from sklearn import set_config
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import make_classification

set_config(enable_metadata_routing=True)
X, y = make_classification(10, 4)
sample_weight = np.ones_like(y)
logreg = LogisticRegression()
pipe = Pipeline([("logistic", logreg)])
search = GridSearchCV(pipe, {"logistic__C": [0.1, 1]}, n_jobs=1, cv=3)
logreg.set_fit_request(sample_weight=True)
logreg.set_score_request(sample_weight=True)
search.fit(X, y, sample_weight=sample_weight)
```

### Expected Results

No error is thrown, and `sample_weight` are routed to the `logreg`.  

### Actual Results

```python-traceback
sklearn.exceptions.UnsetMetadataPassedError: [sample_weight] are passed but are not explicitly set as requested or not requested for Pipeline.score, which is used within GridSearchCV.fit. Call `Pipeline.set_score_request({metadata}=True/False)` for each metadata you want to request/ignore.
```

### Versions

```shell
sklearn: 1.7.dev0
```

COMMENT:
cc @StefanieSenger @adrinjalali

Labels: Bug, Metadata Routing
Pipeline score asks to explicitly request sample_weight

### Describe the bug

When using `Pipeline` with metadata routing enabled,  an error is thrown unless we explicitly request `sample_weight` for the `score` method (see example below). But `Pipeline` is just a router (for both the `fit` and `score` methods) and not a consumer of `sample_weight`, so in principle it should not require `sample_weight` to be explicitly requested.

### Steps/Code to Reproduce

```python
import numpy as np
from sklearn import set_config
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import make_classification

set_config(enable_metadata_routing=True)
X, y = make_classification(10, 4)
sample_weight = np.ones_like(y)
logreg = LogisticRegression()
pipe = Pipeline([("logistic", logreg)])
search = GridSearchCV(pipe, {"logistic__C": [0.1, 1]}, n_jobs=1, cv=3)
logreg.set_fit_request(sample_weight=True)
logreg.set_score_request(sample_weight=True)
search.fit(X, y, sample_weight=sample_weight)
```

### Expected Results

No error is thrown, and `sample_weight` are routed to the `logreg`.  

### Actual Results

```python-traceback
sklearn.exceptions.UnsetMetadataPassedError: [sample_weight] are passed but are not explicitly set as requested or not requested for Pipeline.score, which is used within GridSearchCV.fit. Call `Pipeline.set_score_request({metadata}=True/False)` for each metadata you want to request/ignore.
```

### Versions

```shell
sklearn: 1.7.dev0
```

COMMENT:
I _think_ the solution should be very similar to `LogisticRegressionCV`: https://github.com/scikit-learn/scikit-learn/pull/30859

Labels: Bug, Metadata Routing
Pipeline score asks to explicitly request sample_weight

### Describe the bug

When using `Pipeline` with metadata routing enabled,  an error is thrown unless we explicitly request `sample_weight` for the `score` method (see example below). But `Pipeline` is just a router (for both the `fit` and `score` methods) and not a consumer of `sample_weight`, so in principle it should not require `sample_weight` to be explicitly requested.

### Steps/Code to Reproduce

```python
import numpy as np
from sklearn import set_config
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import make_classification

set_config(enable_metadata_routing=True)
X, y = make_classification(10, 4)
sample_weight = np.ones_like(y)
logreg = LogisticRegression()
pipe = Pipeline([("logistic", logreg)])
search = GridSearchCV(pipe, {"logistic__C": [0.1, 1]}, n_jobs=1, cv=3)
logreg.set_fit_request(sample_weight=True)
logreg.set_score_request(sample_weight=True)
search.fit(X, y, sample_weight=sample_weight)
```

### Expected Results

No error is thrown, and `sample_weight` are routed to the `logreg`.  

### Actual Results

```python-traceback
sklearn.exceptions.UnsetMetadataPassedError: [sample_weight] are passed but are not explicitly set as requested or not requested for Pipeline.score, which is used within GridSearchCV.fit. Call `Pipeline.set_score_request({metadata}=True/False)` for each metadata you want to request/ignore.
```

### Versions

```shell
sklearn: 1.7.dev0
```

COMMENT:
Hello everyone. 

I see you've already discussed this issue a bit and I was wondering if anyone is currently working on a fix. I would be interested to look into this, if possible. 

Thanks!

Labels: Bug, Metadata Routing
Pipeline score asks to explicitly request sample_weight

### Describe the bug

When using `Pipeline` with metadata routing enabled,  an error is thrown unless we explicitly request `sample_weight` for the `score` method (see example below). But `Pipeline` is just a router (for both the `fit` and `score` methods) and not a consumer of `sample_weight`, so in principle it should not require `sample_weight` to be explicitly requested.

### Steps/Code to Reproduce

```python
import numpy as np
from sklearn import set_config
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import make_classification

set_config(enable_metadata_routing=True)
X, y = make_classification(10, 4)
sample_weight = np.ones_like(y)
logreg = LogisticRegression()
pipe = Pipeline([("logistic", logreg)])
search = GridSearchCV(pipe, {"logistic__C": [0.1, 1]}, n_jobs=1, cv=3)
logreg.set_fit_request(sample_weight=True)
logreg.set_score_request(sample_weight=True)
search.fit(X, y, sample_weight=sample_weight)
```

### Expected Results

No error is thrown, and `sample_weight` are routed to the `logreg`.  

### Actual Results

```python-traceback
sklearn.exceptions.UnsetMetadataPassedError: [sample_weight] are passed but are not explicitly set as requested or not requested for Pipeline.score, which is used within GridSearchCV.fit. Call `Pipeline.set_score_request({metadata}=True/False)` for each metadata you want to request/ignore.
```

### Versions

```shell
sklearn: 1.7.dev0
```

COMMENT:
Hi, I’d like to work on this issue if it’s still available. Has anyone already begun working on it, or can I take it on? Thanks!

Labels: Bug, Metadata Routing
Pipeline score asks to explicitly request sample_weight

### Describe the bug

When using `Pipeline` with metadata routing enabled,  an error is thrown unless we explicitly request `sample_weight` for the `score` method (see example below). But `Pipeline` is just a router (for both the `fit` and `score` methods) and not a consumer of `sample_weight`, so in principle it should not require `sample_weight` to be explicitly requested.

### Steps/Code to Reproduce

```python
import numpy as np
from sklearn import set_config
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import make_classification

set_config(enable_metadata_routing=True)
X, y = make_classification(10, 4)
sample_weight = np.ones_like(y)
logreg = LogisticRegression()
pipe = Pipeline([("logistic", logreg)])
search = GridSearchCV(pipe, {"logistic__C": [0.1, 1]}, n_jobs=1, cv=3)
logreg.set_fit_request(sample_weight=True)
logreg.set_score_request(sample_weight=True)
search.fit(X, y, sample_weight=sample_weight)
```

### Expected Results

No error is thrown, and `sample_weight` are routed to the `logreg`.  

### Actual Results

```python-traceback
sklearn.exceptions.UnsetMetadataPassedError: [sample_weight] are passed but are not explicitly set as requested or not requested for Pipeline.score, which is used within GridSearchCV.fit. Call `Pipeline.set_score_request({metadata}=True/False)` for each metadata you want to request/ignore.
```

### Versions

```shell
sklearn: 1.7.dev0
```

COMMENT:
@guilhermecsnpeixoto @pedroL0pes this is not a good first issue. Please find an easy good first easy to start with if you're planning to contribute.

Labels: Bug, Metadata Routing
Pipeline score asks to explicitly request sample_weight

### Describe the bug

When using `Pipeline` with metadata routing enabled,  an error is thrown unless we explicitly request `sample_weight` for the `score` method (see example below). But `Pipeline` is just a router (for both the `fit` and `score` methods) and not a consumer of `sample_weight`, so in principle it should not require `sample_weight` to be explicitly requested.

### Steps/Code to Reproduce

```python
import numpy as np
from sklearn import set_config
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import make_classification

set_config(enable_metadata_routing=True)
X, y = make_classification(10, 4)
sample_weight = np.ones_like(y)
logreg = LogisticRegression()
pipe = Pipeline([("logistic", logreg)])
search = GridSearchCV(pipe, {"logistic__C": [0.1, 1]}, n_jobs=1, cv=3)
logreg.set_fit_request(sample_weight=True)
logreg.set_score_request(sample_weight=True)
search.fit(X, y, sample_weight=sample_weight)
```

### Expected Results

No error is thrown, and `sample_weight` are routed to the `logreg`.  

### Actual Results

```python-traceback
sklearn.exceptions.UnsetMetadataPassedError: [sample_weight] are passed but are not explicitly set as requested or not requested for Pipeline.score, which is used within GridSearchCV.fit. Call `Pipeline.set_score_request({metadata}=True/False)` for each metadata you want to request/ignore.
```

### Versions

```shell
sklearn: 1.7.dev0
```

COMMENT:
Hi, thanks for the feedback! I'll try to contribute to a different bug fix then.

Labels: Bug, Metadata Routing