SelectKBest.fit and fit_transform do not work with y=None

### Describe the bug

Per the documentation here: https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectKBest.html#sklearn.feature_selection.SelectKBest.fit
and
https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectKBest.html#sklearn.feature_selection.SelectKBest.fit_transform

It states under "y":

```
The target values (class labels in classification, real numbers in regression). If the selector is unsupervised then y can be set to None.
```

When using y=None, an error is returned saying that it expects an array-like, but got "None"

I'm not sure if this is related in some way to the TfidfVectorizer or if i'm using it incorrectly in some way.

### Steps/Code to Reproduce

```python
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_selection import SelectKBest, chi2
import numpy as np

documents = [
    "this is a test document",
    "this is also a test document",
    "this one is not a test document",
    "this one might be, but not sure",
    "here is one last one"
]

vectorizer = TfidfVectorizer(stop_words='english')
X = vectorizer.fit_transform(documents)

selector = SelectKBest(score_func=chi2, k=2)
selector.fit_transform(X, y=None)
```


### Expected Results

When y is set to None it is expected to be unsupervised, and should be expected to work as per the documentation.

### Actual Results

```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[11], line 17
     14 X = vectorizer.fit_transform(documents)
     16 selector = SelectKBest(score_func=chi2, k=2)
---> 17 selector.fit_transform(X, y=None)
     18 selector.scores_

File /opt/conda/lib/python3.11/site-packages/sklearn/utils/_set_output.py:295, in _wrap_method_output.<locals>.wrapped(self, X, *args, **kwargs)
    293 @wraps(f)
    294 def wrapped(self, X, *args, **kwargs):
--> 295     data_to_wrap = f(self, X, *args, **kwargs)
    296     if isinstance(data_to_wrap, tuple):
    297         # only wrap the first output for cross decomposition
    298         return_tuple = (
    299             _wrap_data_with_container(method, data_to_wrap[0], X, self),
    300             *data_to_wrap[1:],
    301         )

File /opt/conda/lib/python3.11/site-packages/sklearn/base.py:1098, in TransformerMixin.fit_transform(self, X, y, **fit_params)
   1083         warnings.warn(
   1084             (
   1085                 f"This object ({self.__class__.__name__}) has a `transform`"
   (...)
   1093             UserWarning,
   1094         )
   1096 if y is None:
   1097     # fit method of arity 1 (unsupervised transformation)
-> 1098     return self.fit(X, **fit_params).transform(X)
   1099 else:
   1100     # fit method of arity 2 (supervised transformation)
   1101     return self.fit(X, y, **fit_params).transform(X)

File /opt/conda/lib/python3.11/site-packages/sklearn/base.py:1474, in _fit_context.<locals>.decorator.<locals>.wrapper(estimator, *args, **kwargs)
   1467     estimator._validate_params()
   1469 with config_context(
   1470     skip_parameter_validation=(
   1471         prefer_skip_nested_validation or global_skip_validation
   1472     )
   1473 ):
-> 1474     return fit_method(estimator, *args, **kwargs)

File /opt/conda/lib/python3.11/site-packages/sklearn/feature_selection/_univariate_selection.py:567, in _BaseFilter.fit(self, X, y)
    562     X, y = self._validate_data(
    563         X, y, accept_sparse=["csr", "csc"], multi_output=True
    564     )
    566 self._check_params(X, y)
--> 567 score_func_ret = self.score_func(X, y)
    568 if isinstance(score_func_ret, (list, tuple)):
    569     self.scores_, self.pvalues_ = score_func_ret

File /opt/conda/lib/python3.11/site-packages/sklearn/utils/_param_validation.py:186, in validate_params.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
    184 global_skip_validation = get_config()["skip_parameter_validation"]
    185 if global_skip_validation:
--> 186     return func(*args, **kwargs)
    188 func_sig = signature(func)
    190 # Map *args/**kwargs to the function signature

File /opt/conda/lib/python3.11/site-packages/sklearn/feature_selection/_univariate_selection.py:270, in chi2(X, y)
    266     raise ValueError("Input X must be non-negative.")
    268 # Use a sparse representation for Y by default to reduce memory usage when
    269 # y has many unique classes.
--> 270 Y = LabelBinarizer(sparse_output=True).fit_transform(y)
    271 if Y.shape[1] == 1:
    272     Y = Y.toarray()

File /opt/conda/lib/python3.11/site-packages/sklearn/preprocessing/_label.py:329, in LabelBinarizer.fit_transform(self, y)
    309 def fit_transform(self, y):
    310     """Fit label binarizer/transform multi-class labels to binary labels.
    311 
    312     The output of transform is sometimes referred to as
   (...)
    327         will be of CSR format.
    328     """
--> 329     return self.fit(y).transform(y)

File /opt/conda/lib/python3.11/site-packages/sklearn/base.py:1474, in _fit_context.<locals>.decorator.<locals>.wrapper(estimator, *args, **kwargs)
   1467     estimator._validate_params()
   1469 with config_context(
   1470     skip_parameter_validation=(
   1471         prefer_skip_nested_validation or global_skip_validation
   1472     )
   1473 ):
-> 1474     return fit_method(estimator, *args, **kwargs)

File /opt/conda/lib/python3.11/site-packages/sklearn/preprocessing/_label.py:296, in LabelBinarizer.fit(self, y)
    289 if self.sparse_output and (self.pos_label == 0 or self.neg_label != 0):
    290     raise ValueError(
    291         "Sparse binarization is only supported with non "
    292         "zero pos_label and zero neg_label, got "
    293         f"pos_label={self.pos_label} and neg_label={self.neg_label}"
    294     )
--> 296 self.y_type_ = type_of_target(y, input_name="y")
    298 if "multioutput" in self.y_type_:
    299     raise ValueError(
    300         "Multioutput target data is not supported with label binarization"
    301     )

File /opt/conda/lib/python3.11/site-packages/sklearn/utils/multiclass.py:308, in type_of_target(y, input_name)
    301 valid = (
    302     (isinstance(y, Sequence) or issparse(y) or hasattr(y, "__array__"))
    303     and not isinstance(y, str)
    304     or is_array_api_compliant
    305 )
    307 if not valid:
--> 308     raise ValueError(
    309         "Expected array-like (array or non-string sequence), got %r" % y
    310     )
    312 sparse_pandas = y.__class__.__name__ in ["SparseSeries", "SparseArray"]
    313 if sparse_pandas:

ValueError: Expected array-like (array or non-string sequence), got None
```

### Versions

```shell
System:
    python: 3.11.8 | packaged by conda-forge | (main, Feb 16 2024, 20:53:32) [GCC 12.3.0]
executable: /opt/conda/bin/python
   machine: Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.35

Python dependencies:
      sklearn: 1.4.1.post1
          pip: 24.0
   setuptools: 69.2.0
        numpy: 1.26.4
        scipy: 1.13.0
       Cython: None
       pandas: None
   matplotlib: None
       joblib: 1.4.0
threadpoolctl: 3.4.0

Built with OpenMP: True

threadpoolctl info:
       user_api: openmp
   internal_api: openmp
    num_threads: 24
         prefix: libgomp
       filepath: /opt/conda/lib/python3.11/site-packages/scikit_learn.libs/libgomp-a34b3233.so.1.0.0
        version: None

       user_api: blas
   internal_api: openblas
    num_threads: 24
         prefix: libopenblas
       filepath: /opt/conda/lib/python3.11/site-packages/numpy.libs/libopenblas64_p-r0-0cf96a72.3.23.dev.so
        version: 0.3.23.dev
threading_layer: pthreads
   architecture: Prescott

       user_api: blas
   internal_api: openblas
    num_threads: 24
         prefix: libopenblas
       filepath: /opt/conda/lib/python3.11/site-packages/scipy.libs/libopenblasp-r0-24bff013.3.26.dev.so
        version: 0.3.26.dev
threading_layer: pthreads
   architecture: Haswell
```

COMMENT:
Hmm, looking through them it looks like all of the ones provided by scikit learn require labels. Is it implied that one must write their own score_func for unsupervised?

That is fine if that is the case, its just not clear to me from the documentation that, that is the intent.

Labels: Bug, Needs Triage