Talip7/scikit-learn-issues-multilabel huggingface dataset:
⚠️ CI failed on Linux_Docker.debian_atlas_32bit (last failure: Sep 03, 2024) ⚠️

**CI is still failing on [Linux_Docker.debian_atlas_32bit](https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=69809&view=logs&j=aabdcdc3-bb64-5414-b357-ed024fe8659e)** (Sep 03, 2024)
- test_spectral_clustering_with_arpack_amg_solvers
- test_img_to_graph
- test_imputation_mean_median[csc_matrix]
- test_imputation_mean_median[csc_array]
- test_robust_scale_axis1

COMMENT:
Looks like another error with global random state:

```python
loss = 'absolute_error', subsample = 0.5, global_random_seed = 9

    @pytest.mark.parametrize("loss", ("squared_error", "absolute_error", "huber"))
    @pytest.mark.parametrize("subsample", (1.0, 0.5))
    def test_regression_dataset(loss, subsample, global_random_seed):
        # Check consistency on regression dataset with least squares
        # and least absolute deviation.
        ones = np.ones(len(y_reg))
        last_y_pred = None
        for sample_weight in [None, ones, 2 * ones]:
            # learning_rate, max_depth and n_estimators were adjusted to get a mode
            # that is accurate enough to reach a low MSE on the training set while
            # keeping the resource used to execute this test low enough.
            reg = GradientBoostingRegressor(
                n_estimators=30,
                loss=loss,
                max_depth=4,
                subsample=subsample,
                min_samples_split=2,
                random_state=global_random_seed,
                learning_rate=0.5,
            )
    
            reg.fit(X_reg, y_reg, sample_weight=sample_weight)
            leaves = reg.apply(X_reg)
            assert leaves.shape == (100, 30)
    
            y_pred = reg.predict(X_reg)
            mse = mean_squared_error(y_reg, y_pred)
>           assert mse < 0.05
E           assert 0.061502134763447056 < 0.05
```

Labels: Build / CI, Numerical Stability
⚠️ CI failed on Linux_Docker.debian_atlas_32bit (last failure: Sep 03, 2024) ⚠️

**CI is still failing on [Linux_Docker.debian_atlas_32bit](https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=69809&view=logs&j=aabdcdc3-bb64-5414-b357-ed024fe8659e)** (Sep 03, 2024)
- test_spectral_clustering_with_arpack_amg_solvers
- test_img_to_graph
- test_imputation_mean_median[csc_matrix]
- test_imputation_mean_median[csc_array]
- test_robust_scale_axis1

COMMENT:
Those 4 tests fail because of an out of bounds integer `IndexError`.

I confirm I cannot reproduce in a local docker container where I installed the same version of the system packages (`python3-numpy` 1.24.2 in particular) and the other python packages from todays' lock file.

Labels: Build / CI, Numerical Stability
⚠️ CI failed on Linux_Docker.debian_atlas_32bit (last failure: Sep 03, 2024) ⚠️

**CI is still failing on [Linux_Docker.debian_atlas_32bit](https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=69809&view=logs&j=aabdcdc3-bb64-5414-b357-ed024fe8659e)** (Sep 03, 2024)
- test_spectral_clustering_with_arpack_amg_solvers
- test_img_to_graph
- test_imputation_mean_median[csc_matrix]
- test_imputation_mean_median[csc_array]
- test_robust_scale_axis1

COMMENT:
#29771 has fixed the IndexError issues and changed the build name `debian_atlas_32bit` => `debian_32bit`. I am going to close this one since some historical global_random_seed may be due to Atlas. A new issue will be automatically open when/if similar global_random_seed failures happen.

Labels: Build / CI, Numerical Stability
Implement class to reverse item encodings

### Describe the workflow you want to enable

Hi! As a psychologist working with questionnaire data, you will find yourselves in situations where you have to reverse the values in certain variables (i.e. questionnaire items), because they have reversed logic (see [this link](https://psych.hanover.edu/classes/ResearchMethods/jamovi/reliability/reverseScoring.html) for an example).

### Describe your proposed solution

Implement a class that reverses variable values:

```python
class ReverseColumnValues(TransformerMixin,BaseEstimator):
    '''Reverse matrix column values. NaN values stay as they are'''
    
    def fit(self, X, y=None):
        return self
    
    def _reverse_array_values(self,a):
        '''Reverse values in a vector'''
        unique_vals = np.unique(a[~np.isnan(a)])  # Get unique non-nan values
        flip_dict = dict(zip(unique_vals,np.flip(unique_vals)))  # Create flip dictionary
        return np.vectorize(flip_dict.get)(a)  # map old values onto new values

    def _reverse_column_values(self,X):
        '''Reverse values of all columns of a matrix'''
        X_ = np.apply_along_axis(self._reverse_array_values,0,X)
        return X_
    
    def transform(self, X, y=None):
        return self._reverse_column_values(X)
```



### Describe alternatives you've considered, if relevant

Most certainly users would have to use this class as input to `ColumnTransformer` because you only want to reverse certain variables and leave the rest as it is.

### Additional context

Not sure though, if this might out of scope of scikit-learn. If not, maybe there's a contrib project that could use this?

COMMENT:
Could you please provide an example code snippet to show how to use your transformer on toy data to better clarify the purpose of this?

Maybe what you want is an `OrdinalEncoder.inverse_transform`?

Labels: New Feature, Needs Investigation
Implement class to reverse item encodings

### Describe the workflow you want to enable

Hi! As a psychologist working with questionnaire data, you will find yourselves in situations where you have to reverse the values in certain variables (i.e. questionnaire items), because they have reversed logic (see [this link](https://psych.hanover.edu/classes/ResearchMethods/jamovi/reliability/reverseScoring.html) for an example).

### Describe your proposed solution

Implement a class that reverses variable values:

```python
class ReverseColumnValues(TransformerMixin,BaseEstimator):
    '''Reverse matrix column values. NaN values stay as they are'''
    
    def fit(self, X, y=None):
        return self
    
    def _reverse_array_values(self,a):
        '''Reverse values in a vector'''
        unique_vals = np.unique(a[~np.isnan(a)])  # Get unique non-nan values
        flip_dict = dict(zip(unique_vals,np.flip(unique_vals)))  # Create flip dictionary
        return np.vectorize(flip_dict.get)(a)  # map old values onto new values

    def _reverse_column_values(self,X):
        '''Reverse values of all columns of a matrix'''
        X_ = np.apply_along_axis(self._reverse_array_values,0,X)
        return X_
    
    def transform(self, X, y=None):
        return self._reverse_column_values(X)
```



### Describe alternatives you've considered, if relevant

Most certainly users would have to use this class as input to `ColumnTransformer` because you only want to reverse certain variables and leave the rest as it is.

### Additional context

Not sure though, if this might out of scope of scikit-learn. If not, maybe there's a contrib project that could use this?

COMMENT:
@thomasjpfan @JohannesWiesner  Hi can i implement this ?

Labels: New Feature, Needs Investigation
SimpleImputer with the rule strategies median-1/median+1

### Describe the workflow you want to enable

I have run into an issue with `SimpleImputer`. Given a feature of, say, integer type, it is completely reasonable to impute the median to missing values. However, when the overall number of records is even, there is a decent chance, that the median falls between two integers according to the well-known rule (Sorted[N/2-1] + Sorted[N/2])/2. The issue is, that technically, this kind of imputation breaks the domain of the feature, it used to be integer, but now there are spectacular .5 numbers, which can act weirdly in further processing.

Long story, short, when a sequence like 4, 3, ?, 2, 4, 5, 1 is imputed by 3.5, it is not an integer sequence anymore.

### Describe your proposed solution

My recommendation is to introduce something like an "adjusted median", which would ensure that the imputed value is a value of the domain of the feature. My recommendation is to pick Sorted[N/2-1] or Sorted[N/2], whichever has the highest number of occurances in the data. If equal, take the smallest.

Basically the "most_frequent" strategy applied to Sorted[N/2-1] and Sorted[N/2] only.

### Describe alternatives you've considered, if relevant

Alternative solutions and strategy names could work as well. In the problem described above, the issue is that median calculation is limited to its mathematical definition. `np.percentile`, just like percentile functions in `R` offer more flexibility, as what happens in `SimpleImputer` with the `strategy=median` is that the 50% percentile is taken with linear interpolation. `np.percentile` could do it with `nearest` interpolation. I think offering this control would improve the flexibility of the imputer with very little effort.

### Additional context

_No response_

COMMENT:
Thank you for opening the issue! I can see the motivation behind median-1 and median+1. As for scikit-learn's [inclusion criterion](https://scikit-learn.org/dev/faq.html#what-are-the-inclusion-criteria-for-new-algorithms), may you share a reference that showcases or studies imputation using median-1/median+1?

Labels: New Feature, module:impute, Needs Decision - Include Feature
SimpleImputer with the rule strategies median-1/median+1

### Describe the workflow you want to enable

I have run into an issue with `SimpleImputer`. Given a feature of, say, integer type, it is completely reasonable to impute the median to missing values. However, when the overall number of records is even, there is a decent chance, that the median falls between two integers according to the well-known rule (Sorted[N/2-1] + Sorted[N/2])/2. The issue is, that technically, this kind of imputation breaks the domain of the feature, it used to be integer, but now there are spectacular .5 numbers, which can act weirdly in further processing.

Long story, short, when a sequence like 4, 3, ?, 2, 4, 5, 1 is imputed by 3.5, it is not an integer sequence anymore.

### Describe your proposed solution

My recommendation is to introduce something like an "adjusted median", which would ensure that the imputed value is a value of the domain of the feature. My recommendation is to pick Sorted[N/2-1] or Sorted[N/2], whichever has the highest number of occurances in the data. If equal, take the smallest.

Basically the "most_frequent" strategy applied to Sorted[N/2-1] and Sorted[N/2] only.

### Describe alternatives you've considered, if relevant

Alternative solutions and strategy names could work as well. In the problem described above, the issue is that median calculation is limited to its mathematical definition. `np.percentile`, just like percentile functions in `R` offer more flexibility, as what happens in `SimpleImputer` with the `strategy=median` is that the 50% percentile is taken with linear interpolation. `np.percentile` could do it with `nearest` interpolation. I think offering this control would improve the flexibility of the imputer with very little effort.

### Additional context

_No response_

COMMENT:
Do you mean lower median and higher median? I think this could be done by `np.quantile(x, q=0.5, method='lower')` and `np.quantile(x, q=0.5, method='higher')` respectively. Don't know about usefulness of it, though.

Labels: New Feature, module:impute, Needs Decision - Include Feature
Support nullable pandas dtypes in LabelBinarizer

### Describe the workflow you want to enable

I would like to be able to pass the nullable pandas dtypes ("Int64", "Float64", "boolean") into sklearn's LabelBinarizer. Because the dtypes become object dtype when converted to numpy arrays we get `ValueError: Unknown label type:`:

Repro with sklearn 1.2.1:

```python
    import pandas as pd
    import pytest
    from sklearn.preprocessing import LabelBinarizer

    for dtype in ["Int64", "Float64", "boolean"]:

        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)

        lb = LabelBinarizer()

        with pytest.raises(ValueError, match="Unknown label type:"):
            lb.fit(y_true.unique())

```

### Describe your proposed solution

We should get the same behavior as when int64, float64, and bool dtypes are used, which is no error:

```python
    import pandas as pd
    from sklearn.preprocessing import LabelBinarizer

    for dtype in ["int64", "float64", "bool"]:
        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)

        lb = LabelBinarizer()

        lb.fit(y_true.unique())
        y_one_hot_true = lb.transform(y_true)
```

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

Our current workaround is to convert the data to numpy arrays with the corresponding dtype that works prior to passing it into LabelBinarizer

### Additional context

_No response_

COMMENT:
As noted in https://github.com/scikit-learn/scikit-learn/issues/25634#issuecomment-1435341844, I opened #25638 to resolve this issue.

Labels: New Feature, Pandas compatibility
Support nullable pandas dtypes in LabelBinarizer

### Describe the workflow you want to enable

I would like to be able to pass the nullable pandas dtypes ("Int64", "Float64", "boolean") into sklearn's LabelBinarizer. Because the dtypes become object dtype when converted to numpy arrays we get `ValueError: Unknown label type:`:

Repro with sklearn 1.2.1:

```python
    import pandas as pd
    import pytest
    from sklearn.preprocessing import LabelBinarizer

    for dtype in ["Int64", "Float64", "boolean"]:

        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)

        lb = LabelBinarizer()

        with pytest.raises(ValueError, match="Unknown label type:"):
            lb.fit(y_true.unique())

```

### Describe your proposed solution

We should get the same behavior as when int64, float64, and bool dtypes are used, which is no error:

```python
    import pandas as pd
    from sklearn.preprocessing import LabelBinarizer

    for dtype in ["int64", "float64", "bool"]:
        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)

        lb = LabelBinarizer()

        lb.fit(y_true.unique())
        y_one_hot_true = lb.transform(y_true)
```

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

Our current workaround is to convert the data to numpy arrays with the corresponding dtype that works prior to passing it into LabelBinarizer

### Additional context

_No response_

COMMENT:
@thomasjpfan I'm still seeing the same errors from my reproduction above with sklearn 1.2.2 which has the code from  https://github.com/scikit-learn/scikit-learn/pull/25638. 

It'd odd, because the error is coming from `unique_labels`, which was a related issue https://github.com/scikit-learn/scikit-learn/issues/25634#issuecomment-1435341844 that is, indeed, fixed.

Labels: New Feature, Pandas compatibility
Support nullable pandas dtypes in LabelBinarizer

### Describe the workflow you want to enable

I would like to be able to pass the nullable pandas dtypes ("Int64", "Float64", "boolean") into sklearn's LabelBinarizer. Because the dtypes become object dtype when converted to numpy arrays we get `ValueError: Unknown label type:`:

Repro with sklearn 1.2.1:

```python
    import pandas as pd
    import pytest
    from sklearn.preprocessing import LabelBinarizer

    for dtype in ["Int64", "Float64", "boolean"]:

        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)

        lb = LabelBinarizer()

        with pytest.raises(ValueError, match="Unknown label type:"):
            lb.fit(y_true.unique())

```

### Describe your proposed solution

We should get the same behavior as when int64, float64, and bool dtypes are used, which is no error:

```python
    import pandas as pd
    from sklearn.preprocessing import LabelBinarizer

    for dtype in ["int64", "float64", "bool"]:
        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)

        lb = LabelBinarizer()

        lb.fit(y_true.unique())
        y_one_hot_true = lb.transform(y_true)
```

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

Our current workaround is to convert the data to numpy arrays with the corresponding dtype that works prior to passing it into LabelBinarizer

### Additional context

_No response_

COMMENT:

Even tho https://github.com/scikit-learn/scikit-learn/pull/25638 was placed on the 1.2.2 milestone, it was not cherry picked onto `1.2.2`. If we have a `1.2.3` release, we can include it there.
 It's in the release, but the issue still remains.

With 1.2.2, passing in `y_true` directly works:

```python
import pandas as pd
from sklearn.preprocessing import LabelBinarizer

for dtype in ["Int64", "Float64", "boolean"]:

    y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)

    lb = LabelBinarizer()

    lb.fit(y_true)
```

but calling `y_true.unique()` does not right not. So I'll reopen this issue open.

Labels: New Feature, Pandas compatibility
Support nullable pandas dtypes in LabelBinarizer

### Describe the workflow you want to enable

I would like to be able to pass the nullable pandas dtypes ("Int64", "Float64", "boolean") into sklearn's LabelBinarizer. Because the dtypes become object dtype when converted to numpy arrays we get `ValueError: Unknown label type:`:

Repro with sklearn 1.2.1:

```python
    import pandas as pd
    import pytest
    from sklearn.preprocessing import LabelBinarizer

    for dtype in ["Int64", "Float64", "boolean"]:

        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)

        lb = LabelBinarizer()

        with pytest.raises(ValueError, match="Unknown label type:"):
            lb.fit(y_true.unique())

```

### Describe your proposed solution

We should get the same behavior as when int64, float64, and bool dtypes are used, which is no error:

```python
    import pandas as pd
    from sklearn.preprocessing import LabelBinarizer

    for dtype in ["int64", "float64", "bool"]:
        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)

        lb = LabelBinarizer()

        lb.fit(y_true.unique())
        y_one_hot_true = lb.transform(y_true)
```

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

Our current workaround is to convert the data to numpy arrays with the corresponding dtype that works prior to passing it into LabelBinarizer

### Additional context

_No response_

COMMENT:
I opened https://github.com/scikit-learn/scikit-learn/pull/25638 to fix the issue.

Labels: New Feature, Pandas compatibility
Support nullable pandas dtypes in `confusion_matrix`

### Describe the workflow you want to enable

I would like to be able to pass the nullable pandas dtypes ("Int64", "Float64", "boolean") into sklearn's `confusion_matrix` function. Because the dtypes become object dtype when converted to numpy arrays we get `ValueError: Classification metrics can't handle a mix of unknown and binary targets`:

Repro with sklearn 1.2.1:
```python
    import pandas as pd
    import pytest
    from sklearn.metrics import confusion_matrix

    for dtype in ["Int64", "Float64", "boolean"]:
        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)
        y_predicted = pd.Series([0, 0, 1, 1, 0, 1, 1, 1, 1], dtype="int64")

        with pytest.raises(ValueError, match="Classification metrics can't handle a mix of unknown and binary targets"):
            confusion_matrix(y_true, y_predicted)
```

### Describe your proposed solution

We should get the same behavior as when int64, float64, and bool dtypes are used, which is no error:

```python
    import pandas as pd
    from sklearn.metrics import confusion_matrix

    for dtype in ["int64", "float64", "bool"]:
        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)
        y_predicted = pd.Series([0, 0, 1, 1, 0, 1, 1, 1, 1], dtype="int64")

        confusion_matrix(y_true, y_predicted)
```

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

Our current workaround is to convert the data to numpy arrays with the corresponding dtype that works prior to passing it into `confusion_matrix`

### Additional context

_No response_

COMMENT:
As noted in https://github.com/scikit-learn/scikit-learn/issues/25634#issuecomment-1435341844, I opened #25638 to resolve this issue.

Labels: New Feature, Pandas compatibility
Support nullable pandas dtypes in `unique_labels`

### Describe the workflow you want to enable

I would like to be able to pass the nullable pandas dtypes ("Int64", "Float64", "boolean") into sklearn's `unique_labels` function. Because the dtypes become `object` dtype when converted to numpy arrays we get `ValueError: Mix type of y not allowed, got types {'binary', 'unknown'}`:

Repro with sklearn 1.2.1
```py 
    import pandas as pd
    import pytest
    from sklearn.utils.multiclass import unique_labels
    
    for dtype in ["Int64", "Float64", "boolean"]:
        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)
        y_predicted = pd.Series([0, 0, 1, 1, 0, 1, 1, 1, 1], dtype="int64")

        with pytest.raises(ValueError, match="Mix type of y not allowed, got types"):
            unique_labels(y_true, y_predicted)
```

### Describe your proposed solution

We should get the same behavior as when `int64`, `float64`, and `bool` dtypes are used, which is no error:  

```python
    import pandas as pd
    from sklearn.utils.multiclass import unique_labels
    
    for dtype in ["int64", "float64", "bool"]:
        y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype=dtype)
        y_predicted = pd.Series([0, 0, 1, 1, 0, 1, 1, 1, 1], dtype="int64")

        unique_labels(y_true, y_predicted)
```

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

Our current workaround is to convert the data to numpy arrays with the corresponding dtype that works prior to passing it into `unique_labels`.

### Additional context

_No response_

COMMENT:
Thank you for opening the issue! The underlying issue comes from how pandas exposes `__array__` to be object dtypes with nullable dtypes:

```python
import pandas as pd

y_true = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype="Int64")
print(np.asarray(y_true).dtype)
# object

y_pred = pd.Series([1, 0, 0, 1, 0, 1, 1, 0, 1], dtype="int64")
print(np.asarray(y_pred).dtype)
# int64
```

Scikit-learn already has `check_array` that handles converting pandas nullable dtypes into their corresponding NumPy dytpes, so I opened #25638 to use `check_array` to fix this issue.

Labels: New Feature, Pandas compatibility