Talip7/scikit-learn-issues-multilabel huggingface dataset:
Add an example showcasing the HistGradientBoosting...

### Describe the issue linked to the documentation

The documentation is not putting forward the HGBT very much. People are not guided to it, while it is arguably the most useful model in scikit-learn

One paradigmatic problem is that on the front-page we have a nice visual on regression, but it point to an AdaBoost example :crying_cat_face: 
![image](https://github.com/scikit-learn/scikit-learn/assets/208217/b1bebf8c-168c-4da5-884d-8e3b8ab6c0e6)
I tried having it point to a good didactic example on the HGBT but I couldn't find one.

### Suggest a potential alternative/fix

We should create an example dedicating to showcasing the HBGT.

I think that it should start with a simple didactic example + plot (a plot a bit like the one above would be nice). It should then (in separate section) showcase a few other nice features, for instance:

- Quantile regression
- Support of missing values
- Support of categorical values
- Monotonicity constraints

There are already examples on the various features that I listed above. Ideally, I like not to add examples (we have too many). I'm not sure whether we should merge these in (not convinced, as the corresponding examples are probably too detailed) or point too them.

I would also make sure that this example is linked to from a bunch of places (for instance from the various examples on gradient boosting, just to make sure that people do find the HGBT)

Also, we should link from the HGBT docs to this example and vice-versa

- [ ] Do the example
- [ ] Replace the landing-page figure and link to example
- [ ] Cross-link in the documentation
- [ ] Cross-link in other examples

cc @ArturoAmorQ

COMMENT:
@Snerdify, we already have https://github.com/scikit-learn/scikit-learn/pull/26991 to address this issue

Labels: Documentation
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
Hi @adrinjalali . I am new to open source contributions and looking for an opportunity to get started with it  . Let me know if I can work on this issue .

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
We still don't have a consensus here on how to proceed @adarsh-meher , It's better to work on issues which are marked as "help wanted" and "good first issue".

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
All the `Stratified*Split` CVs assume that is it stratifying on `y`. In the ML context, I think this is the common use of the term "stratify". If there needs to be other "grouping information" then it becomes a "Group*CV".

`train_test_split` needs a `stratify` parameter, because it can split any number of arrays, so there is no way to tell which array is the target. Looking through the codebase, only `resample` and `train_test_split` has a `stratify` parameter because of the same reason.

Currently, I am -1 on the proposal of adding `strata` to `Stratified*Split` CVs.

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
@thomasjpfan `Group*CV` and `Stratified*CV` are pretty much on the opposite side of one another. One makes sure of each group to be exactly in one of train and test sets, the other makes sure each group is split proportionately in train and test sets.

Imagine you have a dataset where one attribute is the state (location) of the event, and due to certain issues (laws / etc) you want to have a similar proportions of all states' data in train and test, then you stratify on state rather than output.

Another example is when you have train / flight data, and each route has its own peculiarities, but you want to train on the whole dataset rather than a single route, while having enough data from each route on train and test; here you'd stratify on "route", while `y` might be "delay".

I'm happy to move this to `Group*CV`, but then we'd need to add a parameter to decide whether the split needs to keep each group in one of train / test, or to keep them proportionate for each train / test.

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
Just a quick comment from a regular user:

+1 can confirm that the proposed feature has a use case that's prevalent in healthcare: stratifying across protected characteristics such as demographics (usually different from the target). (Note we'd usually *also* want to group by patient). 

Also would humbly agree `strata` is a more informative name than `stratify`.

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
I see the use cases and now +1 on introducing `strata` to the splitters.

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
> I'm happy to move this to Group*CV, but then we'd need to add a parameter to decide whether the split needs to keep each group in one of train / test, or to keep them proportionate for each train / test.

The purpose of the `Group*CV` is the opposite: generalize across groups. So I would not imply that stratification is related to what we currently name "group*" splitters.

However, some people might want left-out groupwise splitting on some metadata and stratification on some other metadata, no?

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
For me, this is what I'd aim for I think:

```py
cv = KFold(shuffle=True, n_folds=5).set_split_request(groups=True, strata=True)

GridSearchCV(..., cv=cv).fit(X, y, strata=strata_var, groups=groups_var)

# or
cross_validate(..., cv=cv, params={"groups": groups_var, "strata": strata_var})
```

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
While I am not fundamentally opposed to this feature, I wonder what concrete need it fulfills and what precise statistical question it answers. If we do decide to implement it, we should start with a convincing practical usage example that highlights how the lack of stratification in the CV evaluation loop would have otherwise led to mistaken conclusions.

Disclaimer: at this time I do not see how to come up with such an example, but I would love to learn something.

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
@j-at-ch do you have something at hand as an example to answer @ogrisel ? I don't have a dataset right now, I've only used it as you say in healthcare related work and seen it in other areas as well, but no dataset at hand.

@ogrisel, on the other hand, you can believe us when we say this is what we do / have done in practice.

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
> @ogrisel, on the other hand, you can believe us when we say this is what we do / have done in practice.

It's really not a problem of trust but more of a problem of transparency and education. We should not offer newt tools for our users to shoot themselves in the foot without a clear notice that documents proper use and known pitfalls.

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
+1 from me. Also without a dataset. The interesting ones are most not public.

The arg should be named something like `stratum` or `stratum_id` (or strata). Ideally, the default is such that for classification it uses the target classes/labels.
As I would rather prefer to reduce the amount of splitters, I‘d favor to introduce it as new arg instead of a new splitter.

> We should not offer newt tools for our users to shoot themselves in the foot without a clear notice that documents proper use and known pitfalls.

I don’t think we should be overprotective like that.

Labels: New Feature, RFC
Add `strata` to `Stratified*Split` CV splitters

Right now `Stratified*Split` classes take `y` as the strata, while that's not always the case.

In `train_test_split` we allow a `stratify` arg (which I'm wondering if it should be called `strata`), which defines the groups samples belong to. And inside, we basically do this:

```py
    cv = StratifiedShuffleSplit(test_size=n_test, train_size=n_train, random_state=random_state)

    train, test = next(cv.split(X=arrays[0], y=stratify))

    return list(
        chain.from_iterable(
            (_safe_indexing(a, train), _safe_indexing(a, test)) for a in arrays
        )
    )
```

As you can see, we're passing `stratify` as `y` to the splitter. I think it would make sense to add a `strata` arg to the splitters, and if `None`, we'd take values in `y` instead, as it is now.

Note that now that we have SLEP6, we won't need a separate class for them, and we'd simply request `strata` for the splitter:

```py
cv = StratifiedShuffleSplit().set_split_request(strata=True)
...
GridSearchCV(model, param_grid, cv=cv).fit(X, y, strata=strata_values)
cross_validate(model, X, y, cv=cv, props={"strata": strata_values})
```

cc @marenwestermann

COMMENT:
> I don’t think we should be overprotective like that.

I sincerely believe that any extra code maintenance complexity or usage cognitive load should be matched with demonstrable added user value that can be easily understood by both users and (future) contributors, e.g. via a short example (or preferably by extending an existing example), even if only on synthetic data in the event no suitable public dataset can be found in openml.org or similar.

Labels: New Feature, RFC