Talip7/scikit-learn-issues-multilabel huggingface dataset:
Add `as_frame` to `sklearn.datasets.fetch_20newsgroups`

### Describe the workflow you want to enable

I would like to load the 20 newsgroups dataset as a dataframe in one line of code:
```
X, y = sklearn.datasets.fetch_20newsgroups(return_X_y=True, as_frame=True)
```

### Describe your proposed solution

Add the argument `as_frame` to `sklearn.datasets.fetch_20newsgroups` to make the function behave similar to other data loading functions such as [`fetch_openml`](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.fetch_openml.html) or [`fetch_covtype`](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.fetch_covtype.html#sklearn.datasets.fetch_covtype).

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

Manually casting to a dataframe:
```
In [11]: X, y = sklearn.datasets.fetch_20newsgroups(return_X_y=True)                                                                                                                                         

In [12]: type(X)                                                                                                                                                                                             
Out[12]: list

In [15]: import pandas as pd                                                                                                                                                                                 

In [19]: pd.DataFrame(X, dtype='str')                                                                                                                                                                        
Out[19]: 
                                                       0
0      From: lerxst@wam.umd.edu (where's my thing)\nS...
1      From: guykuo@carson.u.washington.edu (Guy Kuo)...
2      From: twillis@ec.ecn.purdue.edu (Thomas E Will...
3      From: jgreen@amber (Joe Green)\nSubject: Re: W...
4      From: jcm@head-cfa.harvard.edu (Jonathan McDow...
...                                                  ...
11309  From: jim.zisfein@factory.com (Jim Zisfein) \n...
11310  From: ebodin@pearl.tufts.edu\nSubject: Screen ...
11311  From: westes@netcom.com (Will Estes)\nSubject:...
11312  From: steve@hcrlgw (Steven Collins)\nSubject: ...
11313  From: gunning@cco.caltech.edu (Kevin J. Gunnin...

[11314 rows x 1 columns]

```

### Additional context

The [string dtype](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.StringDtype.html#pandas.StringDtype) has been added in version 1.0 and the [minimal version for pandas in sklearn 1.1 will be 1.0.5](https://scikit-learn.org/dev/install.html#installing-the-latest-release) so I believe this should be possible to add.

CC @louquinze

COMMENT:
We can add an `as_series=False` option that would return a pandas series in the mean time. If #16994 gets merge, we can also add an `as_frame` attribute on top.

Labels: New Feature, module:datasets, Pandas compatibility
Add `as_frame` to `sklearn.datasets.fetch_20newsgroups`

### Describe the workflow you want to enable

I would like to load the 20 newsgroups dataset as a dataframe in one line of code:
```
X, y = sklearn.datasets.fetch_20newsgroups(return_X_y=True, as_frame=True)
```

### Describe your proposed solution

Add the argument `as_frame` to `sklearn.datasets.fetch_20newsgroups` to make the function behave similar to other data loading functions such as [`fetch_openml`](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.fetch_openml.html) or [`fetch_covtype`](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.fetch_covtype.html#sklearn.datasets.fetch_covtype).

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

Manually casting to a dataframe:
```
In [11]: X, y = sklearn.datasets.fetch_20newsgroups(return_X_y=True)                                                                                                                                         

In [12]: type(X)                                                                                                                                                                                             
Out[12]: list

In [15]: import pandas as pd                                                                                                                                                                                 

In [19]: pd.DataFrame(X, dtype='str')                                                                                                                                                                        
Out[19]: 
                                                       0
0      From: lerxst@wam.umd.edu (where's my thing)\nS...
1      From: guykuo@carson.u.washington.edu (Guy Kuo)...
2      From: twillis@ec.ecn.purdue.edu (Thomas E Will...
3      From: jgreen@amber (Joe Green)\nSubject: Re: W...
4      From: jcm@head-cfa.harvard.edu (Jonathan McDow...
...                                                  ...
11309  From: jim.zisfein@factory.com (Jim Zisfein) \n...
11310  From: ebodin@pearl.tufts.edu\nSubject: Screen ...
11311  From: westes@netcom.com (Will Estes)\nSubject:...
11312  From: steve@hcrlgw (Steven Collins)\nSubject: ...
11313  From: gunning@cco.caltech.edu (Kevin J. Gunnin...

[11314 rows x 1 columns]

```

### Additional context

The [string dtype](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.StringDtype.html#pandas.StringDtype) has been added in version 1.0 and the [minimal version for pandas in sklearn 1.1 will be 1.0.5](https://scikit-learn.org/dev/install.html#installing-the-latest-release) so I believe this should be possible to add.

CC @louquinze

COMMENT:
That all makes sense, thank you for your explanation @thomasjpfan and @ogrisel

Labels: New Feature, module:datasets, Pandas compatibility
Don't run the labeller twice on a same PR

Do you think it would be possible for the PR labeller to only run on PRs it has not seen yet ?
Sometimes I find that the labels are not great and try to remove a  label but the bot just rush behind me to add the label again :D 

ping @thomasjpfan

COMMENT:
Also I did not find any label for this kind of issue. Should we make a `github` label ?

Labels: workflow
Don't run the labeller twice on a same PR

Do you think it would be possible for the PR labeller to only run on PRs it has not seen yet ?
Sometimes I find that the labels are not great and try to remove a  label but the bot just rush behind me to add the label again :D 

ping @thomasjpfan

COMMENT:
> Also I did not find any label for this kind of issue.Should we make a github label ?

I feel "github" is too generic. Technically most things we do are on github. :D  The label that fits the best it "Build / CI" that is more on the CI side, but we normally use it for the "Build" part. With that in mind, I added a "workflow" label to cover workflow changes.

As for this issue, the bot was setup to run on updates because the labels can become out of date if files change in the PR. I think this situations does not happen too often, so I am okay with having the bot run only when the PR is opened.

Labels: workflow
Don't run the labeller twice on a same PR

Do you think it would be possible for the PR labeller to only run on PRs it has not seen yet ?
Sometimes I find that the labels are not great and try to remove a  label but the bot just rush behind me to add the label again :D 

ping @thomasjpfan

COMMENT:
> I added a "workflow" label to cover workflow changes.

nice :)

Labels: workflow
LabelEncoder not transforming nans as expected.

### Describe the bug

When fitting a LabelEncoder with a pandas Series that contains a nan, transforming an array containing nans fails, even though nan is one of the LabelEncoder classes.

### Steps/Code to Reproduce

```zsh
>>> from sklearn import preprocessing
>>> import pandas as pd
>>> import numpy as np
>>> le = preprocessing.LabelEncoder()
>>> le.fit(pd.Series(["a", "a", "b", np.nan]))
LabelEncoder()
>>> le.classes_
array(['a', 'b', nan], dtype=object)
>>> le.transform(["a"])
array([0])
>>> le.transform([np.nan])
```

### Expected Results

Instead of the error, I'd expect the output to be `array([2])`

### Actual Results

```zsh
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/lib/python3.7/site-packages/sklearn/preprocessing/_label.py", line 138, in transform
    return _encode(y, uniques=self.classes_)
  File "/Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/lib/python3.7/site-packages/sklearn/utils/_encode.py", line 187, in _encode
    diff = _check_unknown(values, uniques)
  File "/Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/lib/python3.7/site-packages/sklearn/utils/_encode.py", line 261, in _check_unknown
    if np.isnan(known_values).any():
TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
```

### Versions

```shell
System:
    python: 3.7.12 (default, Oct 13 2021, 06:53:03)  [Clang 13.0.0 (clang-1300.0.29.3)]
executable: /Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/bin/python
   machine: Darwin-20.6.0-x86_64-i386-64bit

Python dependencies:
          pip: 21.2.4
   setuptools: 57.4.0
      sklearn: 1.0.2
        numpy: 1.21.5
        scipy: 1.7.3
       Cython: None
       pandas: 1.3.5
   matplotlib: None
       joblib: 1.1.0
threadpoolctl: 3.1.0

Built with OpenMP: True
```

COMMENT:
`LabelEncoder.transform` is not using the dtype of `y` seen during `fit`. In other words it sees `[np.nan]` as a float array, which is not compatible with the object array in `classes_`. A workaround is to cast the input to `transform` into an object array:

```python
from sklearn import preprocessing
import pandas as pd
import numpy as np

le = preprocessing.LabelEncoder()
le.fit(pd.Series(["a", "a", "b", np.nan]))

X_trans = le.transform(np.asarray([np.nan], dtype=object))
print(X_trans)
```

Labels: Bug, module:preprocessing
LabelEncoder not transforming nans as expected.

### Describe the bug

When fitting a LabelEncoder with a pandas Series that contains a nan, transforming an array containing nans fails, even though nan is one of the LabelEncoder classes.

### Steps/Code to Reproduce

```zsh
>>> from sklearn import preprocessing
>>> import pandas as pd
>>> import numpy as np
>>> le = preprocessing.LabelEncoder()
>>> le.fit(pd.Series(["a", "a", "b", np.nan]))
LabelEncoder()
>>> le.classes_
array(['a', 'b', nan], dtype=object)
>>> le.transform(["a"])
array([0])
>>> le.transform([np.nan])
```

### Expected Results

Instead of the error, I'd expect the output to be `array([2])`

### Actual Results

```zsh
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/lib/python3.7/site-packages/sklearn/preprocessing/_label.py", line 138, in transform
    return _encode(y, uniques=self.classes_)
  File "/Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/lib/python3.7/site-packages/sklearn/utils/_encode.py", line 187, in _encode
    diff = _check_unknown(values, uniques)
  File "/Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/lib/python3.7/site-packages/sklearn/utils/_encode.py", line 261, in _check_unknown
    if np.isnan(known_values).any():
TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
```

### Versions

```shell
System:
    python: 3.7.12 (default, Oct 13 2021, 06:53:03)  [Clang 13.0.0 (clang-1300.0.29.3)]
executable: /Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/bin/python
   machine: Darwin-20.6.0-x86_64-i386-64bit

Python dependencies:
          pip: 21.2.4
   setuptools: 57.4.0
      sklearn: 1.0.2
        numpy: 1.21.5
        scipy: 1.7.3
       Cython: None
       pandas: 1.3.5
   matplotlib: None
       joblib: 1.1.0
threadpoolctl: 3.1.0

Built with OpenMP: True
```

COMMENT:
Thanks! This is, however, a workaround. Is there any reason why the current behaviour is the desired behaviour and not the one that I want?

Labels: Bug, module:preprocessing
LabelEncoder not transforming nans as expected.

### Describe the bug

When fitting a LabelEncoder with a pandas Series that contains a nan, transforming an array containing nans fails, even though nan is one of the LabelEncoder classes.

### Steps/Code to Reproduce

```zsh
>>> from sklearn import preprocessing
>>> import pandas as pd
>>> import numpy as np
>>> le = preprocessing.LabelEncoder()
>>> le.fit(pd.Series(["a", "a", "b", np.nan]))
LabelEncoder()
>>> le.classes_
array(['a', 'b', nan], dtype=object)
>>> le.transform(["a"])
array([0])
>>> le.transform([np.nan])
```

### Expected Results

Instead of the error, I'd expect the output to be `array([2])`

### Actual Results

```zsh
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/lib/python3.7/site-packages/sklearn/preprocessing/_label.py", line 138, in transform
    return _encode(y, uniques=self.classes_)
  File "/Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/lib/python3.7/site-packages/sklearn/utils/_encode.py", line 187, in _encode
    diff = _check_unknown(values, uniques)
  File "/Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/lib/python3.7/site-packages/sklearn/utils/_encode.py", line 261, in _check_unknown
    if np.isnan(known_values).any():
TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
```

### Versions

```shell
System:
    python: 3.7.12 (default, Oct 13 2021, 06:53:03)  [Clang 13.0.0 (clang-1300.0.29.3)]
executable: /Users/vladimirvargas/.local/share/virtualenvs/real-estate-fGt6EZlF/bin/python
   machine: Darwin-20.6.0-x86_64-i386-64bit

Python dependencies:
          pip: 21.2.4
   setuptools: 57.4.0
      sklearn: 1.0.2
        numpy: 1.21.5
        scipy: 1.7.3
       Cython: None
       pandas: 1.3.5
   matplotlib: None
       joblib: 1.1.0
threadpoolctl: 3.1.0

Built with OpenMP: True
```

COMMENT:
> Is there any reason why the current behaviour is the desired behaviour and not the one that I want?

I think it makes sense to do. I opened https://github.com/scikit-learn/scikit-learn/pull/22629 for a quick fix.

Labels: Bug, module:preprocessing
Add an average callable parameter to sklearn.neighbors.KNeighborsRegressor

### Describe the workflow you want to enable

For some algorithms of kNN it is desired to use other calculations at the end of this method besides using the mean. In some cases using a geometric average or arithmetic-geometric average is needed instead of the regular average.

### Describe your proposed solution

Add a parameter to the KNeighborsRegressor class, for computing the average at the end of the predict() method - something like:

```
# Use the default way to compute the average with a new parameter and string value of 'mean' which would be the default

KNeighborsRegressor(n_neighbors=5, metric=manhattan_distance, weights='uniform', average='mean')
```


```
# Allow callables for the new average parameter...

def geometric_mean(l):
    k=1
    for i in l:
        k*=i
        
    return pow(k, 1/len(l))

KNeighborsRegressor(n_neighbors=5, metric=manhattan_distance, weights='uniform', average=geometric_mean)





# Allow callables for the new average parameter...

def arithmetic_geometric_mean(l):
    tolerance=1e-10
    a0 = mean(l)
    g0 = geometric_mean(l)
    
    an, gn = (a0 + g0) / 2.0, math.sqrt(a0 * g0)
    while abs(an - gn) > tolerance:
        an, gn = (an + gn) / 2.0, math.sqrt(an * gn)
    
    return an

KNeighborsRegressor(n_neighbors=5, metric=manhattan_distance, weights='uniform', average=arithmetic_geometric_mean)
```

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

Not use scikit-learn's KNeighborsRegressor and build my own:

```
#
# Here avg_func is the callable method to compute the average
#
def kNN_regressor(q, R, truth, k, avg_func=my_mean, distance_func=euclidean_distance, **kwargs):
    idx = [0]*k
    dist = [0]*k
    
    for i in range(k):
        idx[i] = i
        if 'minkowski_param' in kwargs:
            mink_p = kwargs['minkowski_param']
            dist[i] = distance_func(q, R[i], mink_p)
        else:
            dist[i] = distance_func(q, R[i])
        
    max_idx = dist.index(max(dist))
        
    for i in range(k, len(R)):
        if 'minkowski_param' in kwargs:
            mink_p = kwargs['minkowski_param']
            d = distance_func(q, R[i], mink_p)
        else:
            d = distance_func(q, R[i])

        if d < dist[max_idx]:
            dist[max_idx] = d
            idx[max_idx] = i
            max_idx = dist.index(max(dist))
    
    t = []
    for i in range(k):
        t.append(truth[idx[i]])
        
    return avg_func(t)
```

### Additional context

_No response_

COMMENT:
I would be happy to implement this. Should I just get started? (First time contributor, don't really know who I should ask.)

Labels: New Feature, module:neighbors, Needs Decision - Include Feature
Add zero_division None or np.nan

### Describe the workflow you want to enable

This would be an extension of my previous addition https://github.com/scikit-learn/scikit-learn/pull/14900, where I added the parameter `zero_division` for precision, recall, f1. Afterwards, it was added for jaccard as well. 

Here, we would return np.nan when the metric is undefined. For the user to force this, she would pass `zero_division=None` or `zero_division=np.nan`. To be clear, **in both cases**, the result would be `np.nan` (even if passing `None`). 

This would be useful if the metric is used downstream for some average, to be excluded, as e.g. pd.mean does. In case the metric included an average, then such behaviour would already be implemented here, ignoring the figures being undefined.

### Describe your proposed solution

Allow zero_division to be `None` or `np.nan`. In both cases, disable the warning as we already do for `zero_divsion` 0 and 

Behaviour for each metric when passing either `None` or `np.nan` as follows. Keep same as today for other values, i.e. back-compatible

Precision:
* If pred_sum = 0, return `np.nan`
* If average != None, ignore from average any class metric being undefined

Recall:
* If true_sum = 0, return `np.nan`
* If average != None, ignore from average any class metric being undefined

F-score:
* if `beta=inf`, return recall, and `beta=0`, return precision
* elif precision=0 or recall=0 (or both), return 0. 
* else return `np.nan`
* If average != None, ignore from average any class metric being undefined

Jaccard:
* if all labels and pred are 0, return `np.nan`
* If average != None, ignore from average any class metric being undefined

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

_No response_

### Additional context

https://github.com/scikit-learn/scikit-learn/issues/14876
https://github.com/scikit-learn/scikit-learn/pull/14900

COMMENT:
From an API point of view, I prefer to only have `zero_division=np.nan` since we are always returning `np.nan`.

The ignoring behavior for `np.nans` seems okay to me.

Labels: New Feature, module:metrics
Add zero_division None or np.nan

### Describe the workflow you want to enable

This would be an extension of my previous addition https://github.com/scikit-learn/scikit-learn/pull/14900, where I added the parameter `zero_division` for precision, recall, f1. Afterwards, it was added for jaccard as well. 

Here, we would return np.nan when the metric is undefined. For the user to force this, she would pass `zero_division=None` or `zero_division=np.nan`. To be clear, **in both cases**, the result would be `np.nan` (even if passing `None`). 

This would be useful if the metric is used downstream for some average, to be excluded, as e.g. pd.mean does. In case the metric included an average, then such behaviour would already be implemented here, ignoring the figures being undefined.

### Describe your proposed solution

Allow zero_division to be `None` or `np.nan`. In both cases, disable the warning as we already do for `zero_divsion` 0 and 

Behaviour for each metric when passing either `None` or `np.nan` as follows. Keep same as today for other values, i.e. back-compatible

Precision:
* If pred_sum = 0, return `np.nan`
* If average != None, ignore from average any class metric being undefined

Recall:
* If true_sum = 0, return `np.nan`
* If average != None, ignore from average any class metric being undefined

F-score:
* if `beta=inf`, return recall, and `beta=0`, return precision
* elif precision=0 or recall=0 (or both), return 0. 
* else return `np.nan`
* If average != None, ignore from average any class metric being undefined

Jaccard:
* if all labels and pred are 0, return `np.nan`
* If average != None, ignore from average any class metric being undefined

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

_No response_

### Additional context

https://github.com/scikit-learn/scikit-learn/issues/14876
https://github.com/scikit-learn/scikit-learn/pull/14900

COMMENT:
ok, cool. I'll work on this and tag you in a PR to review when ready

Labels: New Feature, module:metrics
Add zero_division None or np.nan

### Describe the workflow you want to enable

This would be an extension of my previous addition https://github.com/scikit-learn/scikit-learn/pull/14900, where I added the parameter `zero_division` for precision, recall, f1. Afterwards, it was added for jaccard as well. 

Here, we would return np.nan when the metric is undefined. For the user to force this, she would pass `zero_division=None` or `zero_division=np.nan`. To be clear, **in both cases**, the result would be `np.nan` (even if passing `None`). 

This would be useful if the metric is used downstream for some average, to be excluded, as e.g. pd.mean does. In case the metric included an average, then such behaviour would already be implemented here, ignoring the figures being undefined.

### Describe your proposed solution

Allow zero_division to be `None` or `np.nan`. In both cases, disable the warning as we already do for `zero_divsion` 0 and 

Behaviour for each metric when passing either `None` or `np.nan` as follows. Keep same as today for other values, i.e. back-compatible

Precision:
* If pred_sum = 0, return `np.nan`
* If average != None, ignore from average any class metric being undefined

Recall:
* If true_sum = 0, return `np.nan`
* If average != None, ignore from average any class metric being undefined

F-score:
* if `beta=inf`, return recall, and `beta=0`, return precision
* elif precision=0 or recall=0 (or both), return 0. 
* else return `np.nan`
* If average != None, ignore from average any class metric being undefined

Jaccard:
* if all labels and pred are 0, return `np.nan`
* If average != None, ignore from average any class metric being undefined

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

_No response_

### Additional context

https://github.com/scikit-learn/scikit-learn/issues/14876
https://github.com/scikit-learn/scikit-learn/pull/14900

COMMENT:
I think we can keep the discussion here: https://github.com/scikit-learn/scikit-learn/issues/29048#issuecomment-2460062084

Labels: New Feature, module:metrics