Talip7/scikit-learn-issues-multilabel huggingface dataset:
Consistent HTTP 403 Figshare error since December 27

Seems to be failing since December 27 around 13:00 UTC, see https://github.com/lesteve/test-figshare-fetchers/actions.

I can reproduce locally:
```
❯ curl https://figshare.com/ndownloader/files/5976078            
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>
```

I opened a support ticket at https://support.figshare.com, let's see what they say ...

https://github.com/scikit-learn/scikit-learn/issues/32710 is an example where it hits us ([build log](https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=83721&view=logs&j=dde5042c-7464-5d47-9507-31bdd2ee0a3a&t=4bd2dad8-62b3-5bf9-08a5-a9880c530c94)).

For now CircleCI does not seem affected, thanks to caching likely.

COMMENT:
> For now CircleCI does not seem affected, thanks to caching likely.

it is affected in custom builds that load the california dataset, e.g. https://ndownloader.figshare.com/files/5976036
as used in @omegaml builds

I can reproduce locally, so it does not seem to be specific to IP filtering

```
$ curl https://ndownloader.figshare.com/files/5976036
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

```

Labels: Bug
Consistent HTTP 403 Figshare error since December 27

Seems to be failing since December 27 around 13:00 UTC, see https://github.com/lesteve/test-figshare-fetchers/actions.

I can reproduce locally:
```
❯ curl https://figshare.com/ndownloader/files/5976078            
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>
```

I opened a support ticket at https://support.figshare.com, let's see what they say ...

https://github.com/scikit-learn/scikit-learn/issues/32710 is an example where it hits us ([build log](https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=83721&view=logs&j=dde5042c-7464-5d47-9507-31bdd2ee0a3a&t=4bd2dad8-62b3-5bf9-08a5-a9880c530c94)).

For now CircleCI does not seem affected, thanks to caching likely.

COMMENT:
This is the answer I got in my Figshare support ticket:

> Unfortunately, we had to stop any downloads via scripting methods or CURL commands. The download at the moment can be done only via the
> website interface.

> We are working to fix these problems, but unfortunately,y we don't have an estimation for when these download options will work again.

I tried to ask for a bit more details on a very rough timeline (is it a matter of days, weeks, months?) to help us decide if we want some work-around for the scikit-learn CI at least.

A while ago, I created https://github.com/lesteve/scikit_learn_data with the content of my `~/scikit_learn_data` folder. Not sure how complete this is, but maybe it can be of some use for us in the future ...

> [!WARNING]
> `~/scikit_learn_data` contains pickle files that will be loaded when you use `sklearn.datasets.fetch_*`, which means it could run arbitrary code. Only use https://github.com/lesteve/scikit_learn_data if you have a good reason to trust me and don't use a `scikit_learn_data` folder from a stranger on the internet.

Labels: Bug
Consistent HTTP 403 Figshare error since December 27

Seems to be failing since December 27 around 13:00 UTC, see https://github.com/lesteve/test-figshare-fetchers/actions.

I can reproduce locally:
```
❯ curl https://figshare.com/ndownloader/files/5976078            
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>
```

I opened a support ticket at https://support.figshare.com, let's see what they say ...

https://github.com/scikit-learn/scikit-learn/issues/32710 is an example where it hits us ([build log](https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=83721&view=logs&j=dde5042c-7464-5d47-9507-31bdd2ee0a3a&t=4bd2dad8-62b3-5bf9-08a5-a9880c530c94)).

For now CircleCI does not seem affected, thanks to caching likely.

COMMENT:
Here is a bit more details, I didn't manage some kind of rough timeline 😓:

> It is a temporary problem. 
> Both Figshare and Dimensions have experienced sustained Distributed Denial of Service attacks recently.
> The teams are working tirelessly to limit any impact.

Hopefully this will be sorted out soonish.

In the mean time the CircleCI CI has started to fail on `main` today, see [build log](https://app.circleci.com/pipelines/github/scikit-learn/scikit-learn/74577/workflows/1264ee85-b8cd-4682-b285-bc181cef96f2/jobs/333160). Looks like the cache can not be found, maybe it is invalidated after a few days, not idea ...

I won't be able to take a look before beginning of January and, anyway, looks like our best course of action is to wait and hope that Figshare solves the issue.

Labels: Bug
⚠️ CI failed on macOS.pylatest_conda_forge_mkl_no_openmp (last failure: Dec 28, 2025) ⚠️

**CI failed on [macOS.pylatest_conda_forge_mkl_no_openmp](https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=83721&view=logs&j=8b4d29c4-7356-5617-94d6-7bef17a56394)** (Dec 28, 2025)
Unable to find junit file. Please see link for details.

COMMENT:
## CI is no longer failing! ✅

[Successful run](https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=83740&view=logs&j=8b4d29c4-7356-5617-94d6-7bef17a56394) on Dec 29, 2025

Labels: Needs Triage
⚠️ CI failed on macOS.pylatest_conda_forge_mkl_no_openmp (last failure: Dec 28, 2025) ⚠️

**CI failed on [macOS.pylatest_conda_forge_mkl_no_openmp](https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=83721&view=logs&j=8b4d29c4-7356-5617-94d6-7bef17a56394)** (Dec 28, 2025)
Unable to find junit file. Please see link for details.

COMMENT:
December 28 was a temporary glitch during conda env creation
```
+ conda create --quiet --name testvenv --file ./build_tools/azure/pylatest_conda_forge_mkl_no_openmp_osx-64_conda.lock
Retrieving notices: ...working... done

('Connection broken: IncompleteRead(10731520 bytes read, 4594244 more expected)', IncompleteRead(10731520 bytes read, 4594244 more expected))
('Connection broken: IncompleteRead(10731520 bytes read, 4594244 more expected)', IncompleteRead(10731520 bytes read, 4594244 more expected))

```

Labels: Needs Triage
DOC: First example for SimpleImputer is unclear

### Describe the issue linked to the documentation

The first example for SimpleImputer in 7.4.2. Univariate feature imputation (https://scikit-learn.org/stable/modules/impute.html#univariate-feature-imputation) is bit unclear. While the imputation is done on an array mentioned in the beginning of the example, the transformation is applied on an array defined in next line. It can be rewritten for clarity as below, which clearly shows the transformation.

```
...
 >>> imp = SimpleImputer(missing_values=np.nan, strategy='mean')
 >>> X = [[1.,24],[2.,np.nan],[4.,16.]] 
 >>> imp.fit(X)
   SimpleImputer()
 >>> print(imp.transform(X))
    [[1. 24.]
     [2. 20.]
     [4. 16.]]
```

### Suggest a potential alternative/fix

_No response_

COMMENT:
Hi! I'm a Data Scientist interested in contributing to scikit-learn. I've identified the typo in the SimpleImputer documentation mentioned here and would like to submit a fix for it. Please let me know if I can go ahead!

Labels: Documentation
DOC: First example for SimpleImputer is unclear

### Describe the issue linked to the documentation

The first example for SimpleImputer in 7.4.2. Univariate feature imputation (https://scikit-learn.org/stable/modules/impute.html#univariate-feature-imputation) is bit unclear. While the imputation is done on an array mentioned in the beginning of the example, the transformation is applied on an array defined in next line. It can be rewritten for clarity as below, which clearly shows the transformation.

```
...
 >>> imp = SimpleImputer(missing_values=np.nan, strategy='mean')
 >>> X = [[1.,24],[2.,np.nan],[4.,16.]] 
 >>> imp.fit(X)
   SimpleImputer()
 >>> print(imp.transform(X))
    [[1. 24.]
     [2. 20.]
     [4. 16.]]
```

### Suggest a potential alternative/fix

_No response_

COMMENT:
Hi @sateeshkumarb, thanks for caring, but the example is written this way on purpose to illustrate that fit and transform can (and often will) be applied to different datasets. Think of fitting on `X_train` and transforming `X_test` or some new data later.

Using two different arrays here is meant to highlight that behavior. We often do that throughout the scikit-learn docs.

For that reason, I don't think this needs to be changed, and I will close this issue.

Labels: Documentation
Add Net Benefit for classifications metrics

### Describe the workflow you want to enable

Net benefit is defined as a decision-analytic measure that quantifies the clinical value of a prediction model, test, or marker by putting its benefits and harms on the same scale. This is done by specifying an exchange rate—a clinical judgment about how much a harm (e.g., an unnecessary biopsy) can be tolerated in order to gain a benefit (e.g., detecting a cancer). The net benefit calculation then subtracts weighted harms from benefits to determine whether using the model or test would do more good than harm in practice, unlike traditional statistics such as sensitivity or AUC that do not directly reflect clinical value.

https://www.bmj.com/content/352/bmj.i6



### Describe your proposed solution

I would like to implement this into `_classification.py` , but want to make sure there's no major objection for adding new metrics (and later add a decision analysis curve as well: https://en.wikipedia.org/wiki/Decision_curve_analysis)

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

_No response_

### Additional context

_No response_

COMMENT:
@urigott Thanks for this issue description and the literature reference which is new to me.
As you noted yourself, this is a duplicate of #22136.

Labels: New Feature, Needs Triage
BUG: HuberRegressor fails with boolean feature matrix

### Summary
`HuberRegressor.fit` raises a `TypeError` when the feature matrix is boolean because the internal gradient helper negates a boolean mask with the `-` operator. In NumPy >=1.13 this triggers `TypeError: The numpy boolean negative, the '-' operator, is not supported, use the '~' operator or the logical_not function instead.`

### Steps to reproduce
```python
import numpy as np
from sklearn.linear_model import HuberRegressor

X = np.array([[True, False], [False, True], [True, True]])
y = np.array([1.0, -1.0, 0.5])
HuberRegressor().fit(X, y)
```

### Current behavior
The call fails while computing the gradient due to negation of a boolean mask inside `_huber_loss_and_gradient`.

### Expected behavior
The estimator should accept boolean feature matrices just like other linear models do, internally casting them to floating point types before optimization.

### Environment
- agyn-sandbox/scikit-learn legacy toolchain (Python 3.8.20, NumPy 1.19.5, SciPy 1.7.1)
- master branch as of today

COMMENT:
Hello ! I’d like to work on fixing this issue. 
Would you prefer that boolean inputs be automatically cast to a numeric dtype (float) early in fit(), or should the internal gradient logic be adjusted to avoid unsupported negation of boolean masks? 
Thank you

Labels: Needs Triage
clone: treat class-valued parameters as non-estimators (avoid TypeError) [swev-id: scikit-learn__scikit-learn-12585]

## User request
Clone fails for parameters that are estimator types.

Key: clone should not error when a parameter is a class; adjust clone to treat classes as non-estimators (e.g., check isinstance(estimator, type) or ismethod(get_params)).

## Research specification
- Location: sklearn/base.py, function `clone`.
- Problem: When an estimator has a parameter whose value is a class/type (e.g., `LogisticRegression` rather than an instance), `clone` attempts to call `get_params` on the class object, leading to `TypeError: get_params() missing 1 required positional argument: 'self'`.
- Proposed change: Add an early guard in `clone` after container-type handling:
  - `elif isinstance(estimator, type): return estimator`
  - Treat Python classes/types as non-estimators and leave them unchanged, both at top level and within parameter traversal.
- Edge cases:
  - Estimator parameter set to a scikit-learn estimator class → preserved unchanged.
  - Parameter set to a user-defined class type (not `BaseEstimator`) → preserved unchanged.
  - numpy scalar types (e.g., `np.float64`) → preserved unchanged as types.
  - typing/dataclass types → preserved unchanged.
- Tests:
  - New tests to ensure cloning succeeds and preserves identity for class-valued parameters; estimator instances continue to be cloned.
- Risk & API consistency:
  - Minimal, backward-compatible change; relaxes previous erroneous behavior when encountering class objects. Normal estimator instance cloning behavior is unchanged.

COMMENT:
thanks for reporting this issue

I can reproduce the problem: `clone` treats class-valued parameters as estimators and attempts to call `get_params` on them, which raises a `TypeError`.

I would like to work on this by updating `sklearn.base.clone` to treat Python classes `(isinstance(obj, type))` as non-estimators and return them unchanged, both at top level and when encountered as parameter values. I’ll also add tests to ensure class-valued parameters are preserved while estimator instances continue to be cloned correctly

Labels: Needs Triage
GridSearchCV.verbose documentation does not match actual behavior (values >3, >10)

### Describe the issue linked to the documentation

[`GridSearchCV` docs ](https://scikit-learn.org/1.7/modules/generated/sklearn.model_selection.GridSearchCV.html) state

> **verbose** int
> Controls the verbosity: the higher, the more messages.
> \>1 : the computation time for each fold and parameter candidate is displayed;
> \>2 : the score is also displayed;
> \>3 : the fold and candidate parameter indexes are also displayed together with the starting time of the computation.

In reality, this param is passed to joblib [here](https://github.com/scikit-learn/scikit-learn/blob/d3898d9d57aeb1e960d266613a2e31b07bca39d7/sklearn/model_selection/_search.py#L1009), whose [docs](https://joblib.readthedocs.io/en/latest/generated/joblib.Parallel.html) state:
> **verbose**: int, default=0
> The verbosity level: if non zero, progress messages are printed. Above 50, the output is sent to stdout. The frequency of the messages increases with the verbosity level. If it more than 10, all iterations are reported.

Testing with the MRE below shows that 0 is silent, 1 prints one line, 2 prints a line for each CV split. 3-9 are all the same. 10-inf are all the same

### MRE:
```python
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

X, y = make_classification()

clf = LogisticRegression(max_iter=100)

param_grid = {"C": [1, 2]}

for v in [0, 1, 2, 3, 9, 10, 1000]:
    print(f"\n=======  verbose={v}  ======= ")
    gs = GridSearchCV(
        clf,
        param_grid,
        cv=2,
        n_jobs=1,
        verbose=v,
    )
    gs.fit(X, y)
```

It is possible taht the same issue exists elsewhere, I've not looked thoroughly

### Suggest a potential alternative/fix

Update docs to match observed behaviour, e.g.:

> **verbose** int
> Controls the verbosity: the higher, the more messages.
> 0 : no messages;
> 1 : a single line describing the number of folds, the number of candidates, and the number of fits;
> 2 : the above, plus when each fold ends, the computation time for that fold and parameter candidate is displayed;
> 3-9 : the above, plus the fold index and the score is also displayed;
> ≥10 : an additional message for the start of each fold, and the candidate parameter indexes are also displayed.

COMMENT:
A useful feature would be to have a setting between the current 1 and 2 which prints a message for each param combination, but not for each fold. So it would print the params, and the mean score for each of the metrics over the folds. That would be the ideal setting for me which isn't currently supported. So perhaps this issue should change from the Documentation label to a New Feature Request?

Labels: Documentation
GridSearchCV.verbose documentation does not match actual behavior (values >3, >10)

### Describe the issue linked to the documentation

[`GridSearchCV` docs ](https://scikit-learn.org/1.7/modules/generated/sklearn.model_selection.GridSearchCV.html) state

> **verbose** int
> Controls the verbosity: the higher, the more messages.
> \>1 : the computation time for each fold and parameter candidate is displayed;
> \>2 : the score is also displayed;
> \>3 : the fold and candidate parameter indexes are also displayed together with the starting time of the computation.

In reality, this param is passed to joblib [here](https://github.com/scikit-learn/scikit-learn/blob/d3898d9d57aeb1e960d266613a2e31b07bca39d7/sklearn/model_selection/_search.py#L1009), whose [docs](https://joblib.readthedocs.io/en/latest/generated/joblib.Parallel.html) state:
> **verbose**: int, default=0
> The verbosity level: if non zero, progress messages are printed. Above 50, the output is sent to stdout. The frequency of the messages increases with the verbosity level. If it more than 10, all iterations are reported.

Testing with the MRE below shows that 0 is silent, 1 prints one line, 2 prints a line for each CV split. 3-9 are all the same. 10-inf are all the same

### MRE:
```python
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

X, y = make_classification()

clf = LogisticRegression(max_iter=100)

param_grid = {"C": [1, 2]}

for v in [0, 1, 2, 3, 9, 10, 1000]:
    print(f"\n=======  verbose={v}  ======= ")
    gs = GridSearchCV(
        clf,
        param_grid,
        cv=2,
        n_jobs=1,
        verbose=v,
    )
    gs.fit(X, y)
```

It is possible taht the same issue exists elsewhere, I've not looked thoroughly

### Suggest a potential alternative/fix

Update docs to match observed behaviour, e.g.:

> **verbose** int
> Controls the verbosity: the higher, the more messages.
> 0 : no messages;
> 1 : a single line describing the number of folds, the number of candidates, and the number of fits;
> 2 : the above, plus when each fold ends, the computation time for that fold and parameter candidate is displayed;
> 3-9 : the above, plus the fold index and the score is also displayed;
> ≥10 : an additional message for the start of each fold, and the candidate parameter indexes are also displayed.

COMMENT:
Hi, I’d like to work on improving the documentation for this issue.
Please let me know if that’s okay.

Labels: Documentation
GridSearchCV.verbose documentation does not match actual behavior (values >3, >10)

### Describe the issue linked to the documentation

[`GridSearchCV` docs ](https://scikit-learn.org/1.7/modules/generated/sklearn.model_selection.GridSearchCV.html) state

> **verbose** int
> Controls the verbosity: the higher, the more messages.
> \>1 : the computation time for each fold and parameter candidate is displayed;
> \>2 : the score is also displayed;
> \>3 : the fold and candidate parameter indexes are also displayed together with the starting time of the computation.

In reality, this param is passed to joblib [here](https://github.com/scikit-learn/scikit-learn/blob/d3898d9d57aeb1e960d266613a2e31b07bca39d7/sklearn/model_selection/_search.py#L1009), whose [docs](https://joblib.readthedocs.io/en/latest/generated/joblib.Parallel.html) state:
> **verbose**: int, default=0
> The verbosity level: if non zero, progress messages are printed. Above 50, the output is sent to stdout. The frequency of the messages increases with the verbosity level. If it more than 10, all iterations are reported.

Testing with the MRE below shows that 0 is silent, 1 prints one line, 2 prints a line for each CV split. 3-9 are all the same. 10-inf are all the same

### MRE:
```python
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

X, y = make_classification()

clf = LogisticRegression(max_iter=100)

param_grid = {"C": [1, 2]}

for v in [0, 1, 2, 3, 9, 10, 1000]:
    print(f"\n=======  verbose={v}  ======= ")
    gs = GridSearchCV(
        clf,
        param_grid,
        cv=2,
        n_jobs=1,
        verbose=v,
    )
    gs.fit(X, y)
```

It is possible taht the same issue exists elsewhere, I've not looked thoroughly

### Suggest a potential alternative/fix

Update docs to match observed behaviour, e.g.:

> **verbose** int
> Controls the verbosity: the higher, the more messages.
> 0 : no messages;
> 1 : a single line describing the number of folds, the number of candidates, and the number of fits;
> 2 : the above, plus when each fold ends, the computation time for that fold and parameter candidate is displayed;
> 3-9 : the above, plus the fold index and the score is also displayed;
> ≥10 : an additional message for the start of each fold, and the candidate parameter indexes are also displayed.

COMMENT:
same issue with `RandomizedSearchCV`.

Labels: Documentation
GridSearchCV.verbose documentation does not match actual behavior (values >3, >10)

### Describe the issue linked to the documentation

[`GridSearchCV` docs ](https://scikit-learn.org/1.7/modules/generated/sklearn.model_selection.GridSearchCV.html) state

> **verbose** int
> Controls the verbosity: the higher, the more messages.
> \>1 : the computation time for each fold and parameter candidate is displayed;
> \>2 : the score is also displayed;
> \>3 : the fold and candidate parameter indexes are also displayed together with the starting time of the computation.

In reality, this param is passed to joblib [here](https://github.com/scikit-learn/scikit-learn/blob/d3898d9d57aeb1e960d266613a2e31b07bca39d7/sklearn/model_selection/_search.py#L1009), whose [docs](https://joblib.readthedocs.io/en/latest/generated/joblib.Parallel.html) state:
> **verbose**: int, default=0
> The verbosity level: if non zero, progress messages are printed. Above 50, the output is sent to stdout. The frequency of the messages increases with the verbosity level. If it more than 10, all iterations are reported.

Testing with the MRE below shows that 0 is silent, 1 prints one line, 2 prints a line for each CV split. 3-9 are all the same. 10-inf are all the same

### MRE:
```python
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

X, y = make_classification()

clf = LogisticRegression(max_iter=100)

param_grid = {"C": [1, 2]}

for v in [0, 1, 2, 3, 9, 10, 1000]:
    print(f"\n=======  verbose={v}  ======= ")
    gs = GridSearchCV(
        clf,
        param_grid,
        cv=2,
        n_jobs=1,
        verbose=v,
    )
    gs.fit(X, y)
```

It is possible taht the same issue exists elsewhere, I've not looked thoroughly

### Suggest a potential alternative/fix

Update docs to match observed behaviour, e.g.:

> **verbose** int
> Controls the verbosity: the higher, the more messages.
> 0 : no messages;
> 1 : a single line describing the number of folds, the number of candidates, and the number of fits;
> 2 : the above, plus when each fold ends, the computation time for that fold and parameter candidate is displayed;
> 3-9 : the above, plus the fold index and the score is also displayed;
> ≥10 : an additional message for the start of each fold, and the candidate parameter indexes are also displayed.

COMMENT:
Thanks @StefanieSenger.

> Did you test this with a large dataset and can you verify if there is a gain in information between verbosity=3 and verbosity>10? Meaning, is there a delay between START and END?

Yes, there is a delay between START and END. You can confirm this using the MRE above by changing to 
```python
X, y = make_classification(n_samples=5_000_000)
```
which makes each fold take a second.

Thanks for the info about the callback Issue, that def sounds like a valuable addition to sklearn.

@anandvenugopal-tech - you expressed interest in making a PR for this doc change?

Labels: Documentation