Talip7/scikit-learn-issues-multilabel huggingface dataset:
DOC Managing huntr security vulnerability reports

### Describe the issue linked to the documentation

### Issues
- The project receives reports from huntr that are not useful.
- The reports from huntr are time consuming and use up limited maintainer resources.

### Discussion / Proposal
- Update our [SECURITY.md](http://security.md/) file to indicate how we are dealing with huntr reports
- Direct security reporters to provide more detailed information on security vulnerability including proof of concept (POC) and proof of impact (POI)
- Once POC and POI is established, can direct people to report issue via the GitHub Security Advisory: https://github.com/scikit-learn/scikit-learn/security/advisories/new
- Remove scikit-learn from the huntr bug bounty program


### Proposed text for huntr reports
Draft text for huntr submissions: 
>The scikit-learn project is not reviewing reports submitted to huntr. Please use our SECURITY.md to submit reports. For security reports, provide both a POC (proof of concept) and POI (proof of impact). If your report is deemed impactful, you can then report it to huntr to collect a bounty.

### References
- [Scientific Python SPEC](https://github.com/scientific-python/specs/pull/391/)
- [NumPy discussion on security](https://github.com/numpy/numpy/issues/29178)
- [Dask: comment from huntr person](https://github.com/dask/community/issues/415#issuecomment-2755046159)

### Suggest a potential alternative/fix

_No response_

COMMENT:
OK it looks like this is done so I am going to close this one 🎉.

For further reference here is the email I sent:
```
Dear Adam,

as a joblib and scikit-learn maintainer, I would like that Huntr stops
accepting security reports on joblib and scikit-learn.

I could not find any documentation about the process if there is any,
but let me know if there are additional steps to follow on top of this
email.

The main reasons are:
- the amount of low quality reports is just too high. We had roughly ~20
  reports and maybe 10% of them were not bogus
- we would rather use GitHub security advisories as the only entry point
  for submitting security reports

Cheers,
Loïc Estève
```

And here is the email I got (roughly 2 days afterwards):
```
Hi Loïc,

I'm sorry you've had that experience. I've removed the joblib and scikit-learn OSS repositories from our bug bounty program. You should
no longer receive any new reports with regards to these.

All the best,
Adam
```

Labels: Documentation, Needs Investigation
DOC About Us page - clarify team descriptions

### Describe the issue linked to the documentation

References #31430 

[@thomasjpfan note](https://github.com/scikit-learn/scikit-learn/pull/31430#issuecomment-2914501524):
>With the current governance, all the named roles are considered "core". Specifically, the contributor experience, communication, documentation, and maintainer teams are all "core contributors".

>Before this PR, only the maintainer team can approve PRs. With this PR, any of the other teams can approve PRs. Although, in practice, I think we normally considered the other approvals as valid.

[@ArturoAmorQ note](https://github.com/scikit-learn/scikit-learn/pull/31430#pullrequestreview-2874019392):
>Honest question, shall we modify the terminology across the documentation e.g. in about.rst? Such that it's clear who are those referred here.

Team names and descriptions are not consistent.

1. GitHub Teams: https://github.com/orgs/scikit-learn/teams
- Communication Team
- Contributor Experience Team
- Core-devs
- Documentation Team

2. About Us page: https://scikit-learn.org/dev/about.html

Active Core Contributors
- Maintainers Team
- Documentation Team
- Contributor Experience Team
- Communication Team



### Suggest a potential alternative/fix

_No response_

COMMENT:
@reshamas, I'd be happy to help with this :)

Labels: Documentation
DOC About Us page - clarify team descriptions

### Describe the issue linked to the documentation

References #31430 

[@thomasjpfan note](https://github.com/scikit-learn/scikit-learn/pull/31430#issuecomment-2914501524):
>With the current governance, all the named roles are considered "core". Specifically, the contributor experience, communication, documentation, and maintainer teams are all "core contributors".

>Before this PR, only the maintainer team can approve PRs. With this PR, any of the other teams can approve PRs. Although, in practice, I think we normally considered the other approvals as valid.

[@ArturoAmorQ note](https://github.com/scikit-learn/scikit-learn/pull/31430#pullrequestreview-2874019392):
>Honest question, shall we modify the terminology across the documentation e.g. in about.rst? Such that it's clear who are those referred here.

Team names and descriptions are not consistent.

1. GitHub Teams: https://github.com/orgs/scikit-learn/teams
- Communication Team
- Contributor Experience Team
- Core-devs
- Documentation Team

2. About Us page: https://scikit-learn.org/dev/about.html

Active Core Contributors
- Maintainers Team
- Documentation Team
- Contributor Experience Team
- Communication Team



### Suggest a potential alternative/fix

_No response_

COMMENT:
@reshama I think you mean #25753. What exactly do you proose?
Make the about/governance page clearer (better structure, same bames)? Or change github team titles?

BTW: We should at least have one github team that includes all core contributors at once.

Labels: Documentation
DOC About Us page - clarify team descriptions

### Describe the issue linked to the documentation

References #31430 

[@thomasjpfan note](https://github.com/scikit-learn/scikit-learn/pull/31430#issuecomment-2914501524):
>With the current governance, all the named roles are considered "core". Specifically, the contributor experience, communication, documentation, and maintainer teams are all "core contributors".

>Before this PR, only the maintainer team can approve PRs. With this PR, any of the other teams can approve PRs. Although, in practice, I think we normally considered the other approvals as valid.

[@ArturoAmorQ note](https://github.com/scikit-learn/scikit-learn/pull/31430#pullrequestreview-2874019392):
>Honest question, shall we modify the terminology across the documentation e.g. in about.rst? Such that it's clear who are those referred here.

Team names and descriptions are not consistent.

1. GitHub Teams: https://github.com/orgs/scikit-learn/teams
- Communication Team
- Contributor Experience Team
- Core-devs
- Documentation Team

2. About Us page: https://scikit-learn.org/dev/about.html

Active Core Contributors
- Maintainers Team
- Documentation Team
- Contributor Experience Team
- Communication Team



### Suggest a potential alternative/fix

_No response_

COMMENT:
@lorentzenchr If it's not too much of a disruption, can we:
a) On GitHub Teams, rename "core-devs" to "maintainers-team"
b) Create a new team "core-team" and include all members from 
- "Maintainers Team"
- "Contributor Experience Team"
- "Communication Team"
- "Documentation Team"

Labels: Documentation
Check `pos_label` present in `y_true` in metric functions

Noticed while working on https://github.com/scikit-learn/scikit-learn/pull/30508#discussion_r2158871194

Currently the following metric functions do not explicitly check that `pos_label` is present in `y_true`:

* `roc_curve`
* `precision_recall_curve`
* `det_curve`
* `brier_score_loss`

AFAICT all (?) other classification metrics (e.g., `recall_score`, `precision_score`), including ranking metric `average_precision_score` explicitly check that `pos_label` is present in `y_true`:

e.g. this is the error from `recall_score`/`precision_score`/`f1` family:
```
        if y_type == "binary":
            if len(present_labels) == 2 and pos_label not in present_labels:
>               raise ValueError(
                    f"pos_label={pos_label} is not a valid label. It should be "
                    f"one of {present_labels}"
                )
E               ValueError: pos_label=2 is not a valid label. It should be one of [0, 1]
```

`roc_curve` and `precision_recall_curve` do not explicitly check this, they do *warn* (no error) that there are no 'positive' samples in `y_true`:

```
        if tps[-1] <= 0:
>           warnings.warn(
                "No positive samples in y_true, true positive value should be meaningless",
                UndefinedMetricWarning,
            )
E           sklearn.exceptions.UndefinedMetricWarning: No positive samples in y_true, true positive value should be meaningless
```

Similarly, for `det_curve` this results in an invalid divide warning (we divide by 0):
```
File ~/Documents/dev/scikit-learn/sklearn/metrics/_ranking.py:418, in det_curve(y_true, y_score, pos_label, sample_weight, drop_intermediate)
    415 sl = slice(first_ind, last_ind)
    417 # reverse the output such that list of false positives is decreasing
--> 418 return (fps[sl][::-1] / n_count, fns[sl][::-1] / p_count, thresholds[sl][::-1])

RuntimeWarning: invalid value encountered in divide
```

`brier_score_loss` gives no error and no warning. `_validate_binary_probabilistic_prediction` does the following

https://github.com/scikit-learn/scikit-learn/blob/bde701db13cebd5d5ed0bc049fb0bc9693c6c5dc/sklearn/metrics/_classification.py#L3468-L3469

which just results in `y_true` being all 0's.

For completeness I'm just going to reference #18101, even though I don't think it directly affects this issue; regardless of `pos_label` meaning, the question of whether to check it is present in `y_true` is the same. Just a reference for those who were involved in that discussion, e.g., @glemaitre 😬

COMMENT:
Hey @lucyleeow! I'd like to work on standardizing the behavior across these metrics by adding explicit checks to ensure `pos_label` is present in `y_true`, and raising a `ValueError` when it's not. Would that be a welcome contribution?

Labels: Bug, Needs Decision, module:metrics
Check `pos_label` present in `y_true` in metric functions

Noticed while working on https://github.com/scikit-learn/scikit-learn/pull/30508#discussion_r2158871194

Currently the following metric functions do not explicitly check that `pos_label` is present in `y_true`:

* `roc_curve`
* `precision_recall_curve`
* `det_curve`
* `brier_score_loss`

AFAICT all (?) other classification metrics (e.g., `recall_score`, `precision_score`), including ranking metric `average_precision_score` explicitly check that `pos_label` is present in `y_true`:

e.g. this is the error from `recall_score`/`precision_score`/`f1` family:
```
        if y_type == "binary":
            if len(present_labels) == 2 and pos_label not in present_labels:
>               raise ValueError(
                    f"pos_label={pos_label} is not a valid label. It should be "
                    f"one of {present_labels}"
                )
E               ValueError: pos_label=2 is not a valid label. It should be one of [0, 1]
```

`roc_curve` and `precision_recall_curve` do not explicitly check this, they do *warn* (no error) that there are no 'positive' samples in `y_true`:

```
        if tps[-1] <= 0:
>           warnings.warn(
                "No positive samples in y_true, true positive value should be meaningless",
                UndefinedMetricWarning,
            )
E           sklearn.exceptions.UndefinedMetricWarning: No positive samples in y_true, true positive value should be meaningless
```

Similarly, for `det_curve` this results in an invalid divide warning (we divide by 0):
```
File ~/Documents/dev/scikit-learn/sklearn/metrics/_ranking.py:418, in det_curve(y_true, y_score, pos_label, sample_weight, drop_intermediate)
    415 sl = slice(first_ind, last_ind)
    417 # reverse the output such that list of false positives is decreasing
--> 418 return (fps[sl][::-1] / n_count, fns[sl][::-1] / p_count, thresholds[sl][::-1])

RuntimeWarning: invalid value encountered in divide
```

`brier_score_loss` gives no error and no warning. `_validate_binary_probabilistic_prediction` does the following

https://github.com/scikit-learn/scikit-learn/blob/bde701db13cebd5d5ed0bc049fb0bc9693c6c5dc/sklearn/metrics/_classification.py#L3468-L3469

which just results in `y_true` being all 0's.

For completeness I'm just going to reference #18101, even though I don't think it directly affects this issue; regardless of `pos_label` meaning, the question of whether to check it is present in `y_true` is the same. Just a reference for those who were involved in that discussion, e.g., @glemaitre 😬

COMMENT:
Thanks for your enthusiasm, but let's wait till we decide how we want to handle this.

Labels: Bug, Needs Decision, module:metrics
DOC: Glossary contains several FIXME tags

### Describe the issue linked to the documentation

The Glossary for scikit-learn contains several FIXME tags.
https://scikit-learn.org/dev/glossary.html

### Suggest a potential alternative/fix

FIXME tags can be used for future improvement, but I think they should belong in code comments instead of the Glossary page.

COMMENT:
I would agree, probably should not have 'FIXME' in our glossary.

Maybe they could be moved to issues or, even better, fixed.

Labels: Documentation
DOC: Glossary contains several FIXME tags

### Describe the issue linked to the documentation

The Glossary for scikit-learn contains several FIXME tags.
https://scikit-learn.org/dev/glossary.html

### Suggest a potential alternative/fix

FIXME tags can be used for future improvement, but I think they should belong in code comments instead of the Glossary page.

COMMENT:
need detailed description of the issue

Labels: Documentation
ENH: Add MedianAbsoluteDeviationScaler (MADScaler) to sklearn.preprocessing

### Describe the workflow you want to enable

Today, if a user wants to centre features by the median and scale them by the median absolute deviation (MAD) they must hand-roll code like:
mad = 1.4826 * np.median(np.abs(X - np.median(X, axis=0)), axis=0)
X_scaled = (X - np.median(X, axis=0)) / mad

A built-in MedianAbsoluteDeviationScaler (or a statistic="mad" option on RobustScaler) would let them write a single, self-documenting line:
from sklearn.preprocessing import MedianAbsoluteDeviationScaler
X_scaled = MedianAbsoluteDeviationScaler().fit_transform(X)

That makes robust MAD scaling first-class, composable in pipelines, and reversible via inverse_transform().

### Describe your proposed solution

Add a new transformer:
class MedianAbsoluteDeviationScaler(BaseEstimator, TransformerMixin):
    with_centering: bool = True
    with_scaling:   bool = True
    copy:           bool = True
    unit_variance:  bool = False

    # learned in fit
    center_: ndarray
    scale_: ndarray

Fit logic
1. center_ = np.median(X, axis=0) (if with_centering)

2. mad = np.median(np.abs(X - center_), axis=0) * 1.4826

3. Guard against zeros with float_eps, store in scale_.

transform() and inverse_transform() reuse the pattern from RobustScaler.

Docs / tests

- Unit tests for shape preservation, inverse-transform round-trip, and robustness to outliers.

- A short subsection in preprocessing.rst and a gallery example comparing Standard, Robust (IQR) and MAD scalers.

- Changelog bullet in whats_new/v1.5.rst.

I am happy to implement this within ~2 weeks.

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

- Keep user-land recipes – fragments the ecosystem and lacks inverse_transform().

- Extend RobustScaler with statistic={"iqr","mad"} (default "iqr"). This also works, but changes a long-standing API and may require a deprecation cycle.

### Additional context

- MAD is a well-known σ-consistent robust scale estimator, more efficient than IQR for symmetric heavy-tailed or Laplace-like data.

- Estimated code diff ≈ 20 LOC plus tests/docs – labelled “good first issue” size.

/cc @jnothman @glemaitre for initial design feedback — thanks!

COMMENT:
I think a separate transformer is a better solution, but I'm not sure if it will be useful. I will try to plot it like in [this example](https://scikit-learn.org/stable/auto_examples/preprocessing/plot_all_scaling.html)

I also not sure if this transformer should have multiplier constant, since we do not have one in IQR scaler (`RobustScaler`)

Labels: New Feature, module:preprocessing, Needs Investigation
ENH: Add MedianAbsoluteDeviationScaler (MADScaler) to sklearn.preprocessing

### Describe the workflow you want to enable

Today, if a user wants to centre features by the median and scale them by the median absolute deviation (MAD) they must hand-roll code like:
mad = 1.4826 * np.median(np.abs(X - np.median(X, axis=0)), axis=0)
X_scaled = (X - np.median(X, axis=0)) / mad

A built-in MedianAbsoluteDeviationScaler (or a statistic="mad" option on RobustScaler) would let them write a single, self-documenting line:
from sklearn.preprocessing import MedianAbsoluteDeviationScaler
X_scaled = MedianAbsoluteDeviationScaler().fit_transform(X)

That makes robust MAD scaling first-class, composable in pipelines, and reversible via inverse_transform().

### Describe your proposed solution

Add a new transformer:
class MedianAbsoluteDeviationScaler(BaseEstimator, TransformerMixin):
    with_centering: bool = True
    with_scaling:   bool = True
    copy:           bool = True
    unit_variance:  bool = False

    # learned in fit
    center_: ndarray
    scale_: ndarray

Fit logic
1. center_ = np.median(X, axis=0) (if with_centering)

2. mad = np.median(np.abs(X - center_), axis=0) * 1.4826

3. Guard against zeros with float_eps, store in scale_.

transform() and inverse_transform() reuse the pattern from RobustScaler.

Docs / tests

- Unit tests for shape preservation, inverse-transform round-trip, and robustness to outliers.

- A short subsection in preprocessing.rst and a gallery example comparing Standard, Robust (IQR) and MAD scalers.

- Changelog bullet in whats_new/v1.5.rst.

I am happy to implement this within ~2 weeks.

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

- Keep user-land recipes – fragments the ecosystem and lacks inverse_transform().

- Extend RobustScaler with statistic={"iqr","mad"} (default "iqr"). This also works, but changes a long-standing API and may require a deprecation cycle.

### Additional context

- MAD is a well-known σ-consistent robust scale estimator, more efficient than IQR for symmetric heavy-tailed or Laplace-like data.

- Estimated code diff ≈ 20 LOC plus tests/docs – labelled “good first issue” size.

/cc @jnothman @glemaitre for initial design feedback — thanks!

COMMENT:
So, after running the example, I'm not sure if this transformer adds something new to the library

  - <details><summary>StandardScaler</summary>
    <pre>
    <img src="https://github.com/user-attachments/assets/51a78436-da22-4c78-b4c9-37e5521fafdc" alt="StandardScaler">
    </pre>
   </details>


  - <details><summary>RobustScaler</summary>
    <pre>
    <img src="https://github.com/user-attachments/assets/870ef636-2dfc-4447-a428-a5875e32f703" alt="RobustScaler">
    </pre>
   </details>


  - <details><summary>MADScaler</summary>
    <pre>
    <img src="https://github.com/user-attachments/assets/f55bc41b-db4f-47cb-b33b-beaec8de720d" alt="MADScaler">
    </pre>
   </details>

Labels: New Feature, module:preprocessing, Needs Investigation
ENH: Add MedianAbsoluteDeviationScaler (MADScaler) to sklearn.preprocessing

### Describe the workflow you want to enable

Today, if a user wants to centre features by the median and scale them by the median absolute deviation (MAD) they must hand-roll code like:
mad = 1.4826 * np.median(np.abs(X - np.median(X, axis=0)), axis=0)
X_scaled = (X - np.median(X, axis=0)) / mad

A built-in MedianAbsoluteDeviationScaler (or a statistic="mad" option on RobustScaler) would let them write a single, self-documenting line:
from sklearn.preprocessing import MedianAbsoluteDeviationScaler
X_scaled = MedianAbsoluteDeviationScaler().fit_transform(X)

That makes robust MAD scaling first-class, composable in pipelines, and reversible via inverse_transform().

### Describe your proposed solution

Add a new transformer:
class MedianAbsoluteDeviationScaler(BaseEstimator, TransformerMixin):
    with_centering: bool = True
    with_scaling:   bool = True
    copy:           bool = True
    unit_variance:  bool = False

    # learned in fit
    center_: ndarray
    scale_: ndarray

Fit logic
1. center_ = np.median(X, axis=0) (if with_centering)

2. mad = np.median(np.abs(X - center_), axis=0) * 1.4826

3. Guard against zeros with float_eps, store in scale_.

transform() and inverse_transform() reuse the pattern from RobustScaler.

Docs / tests

- Unit tests for shape preservation, inverse-transform round-trip, and robustness to outliers.

- A short subsection in preprocessing.rst and a gallery example comparing Standard, Robust (IQR) and MAD scalers.

- Changelog bullet in whats_new/v1.5.rst.

I am happy to implement this within ~2 weeks.

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

- Keep user-land recipes – fragments the ecosystem and lacks inverse_transform().

- Extend RobustScaler with statistic={"iqr","mad"} (default "iqr"). This also works, but changes a long-standing API and may require a deprecation cycle.

### Additional context

- MAD is a well-known σ-consistent robust scale estimator, more efficient than IQR for symmetric heavy-tailed or Laplace-like data.

- Estimated code diff ≈ 20 LOC plus tests/docs – labelled “good first issue” size.

/cc @jnothman @glemaitre for initial design feedback — thanks!

COMMENT:
AI Spam

Labels: New Feature, module:preprocessing, Needs Investigation
(Perhaps) safer version of halving search

### Describe the workflow you want to enable

I find the current experimental implementation of HalvingGridSearchCV problematic. At the first rounds it tends to select candidates with hyperparameters adapted to small sample sizes that are bad in hindsight, when it's too late. Think of regularization, tree depth, number of leaves, etc. This is a problem with CV in general, but 4/5 or 9/10 are a far cry from #samples / #candidates.

### Describe your proposed solution

I've the following suggestion, although TBH I haven't thoroughly thought about it: take a splitter as usual and in each iteration of the splitter reduce the candidates, say by 2 or 3. So, for example, you start with cv=5 and 100 candidates, fit them on folds 2-5, compute scores on fold 1, discard half the candidates, proceed to the next split with test fold = 2 and 50 remaining candidates, etc. It obviously requires more resources than the current implementation, but early selected candidates would be better adapted to the last rounds.

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

Implementing the above on top of GridSearchCV.

### Additional context

_No response_

COMMENT:
Halving grid search can use fewer samples or less iterations of the algorithm. For example a smaller random forest or fewer boosting iterations. Choosing the right thing is, unfortunately, something the user has to do themselves.

Implementing a new estimator is a big undertaking and only well established algorithms are considered.

Closing the issue as it hasn't attracted attention since being created.

Labels: New Feature, Needs Triage