Handling `pd.NA` in encoders

It seems that we don't handle it properly `pd.NA` in the encoder and thus differently than `np.nan`.
`pd.NA` will raise an error as in the following reproducible:

```python
df = pd.DataFrame({"col_1": ["A", "B", pd.NA]})
OneHotEncoder(sparse_output=False).fit_transform(df)
```

or 

```python
df = pd.DataFrame({"col_1": ["A", "B", pd.NA]})
OrdinalEncoder().fit_transform(df)
```

leading to:

<details>

```pytb
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File [~/Documents/packages/scikit-learn/sklearn/utils/_encode.py:174](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/~/Documents/packages/scikit-learn/sklearn/utils/_encode.py:174), in _unique_python(values, return_inverse, return_counts)
    [172](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=171) uniques_set, missing_values = _extract_missing(uniques_set)
--> [174](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=173) uniques = sorted(uniques_set)
    [175](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=174) uniques.extend(missing_values.to_list())

File [~/miniconda3/envs/dev/lib/python3.10/site-packages/pandas/_libs/missing.pyx:388](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/~/miniconda3/envs/dev/lib/python3.10/site-packages/pandas/_libs/missing.pyx:388), in pandas._libs.missing.NAType.__bool__()

TypeError: boolean value of NA is ambiguous

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
[/home/glemaitre/Documents/packages/scikit-learn/examples/model_selection/plot_tuned_threshold_classifier_with_metadata_routing.py](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/examples/model_selection/plot_tuned_threshold_classifier_with_metadata_routing.py) in line 3
      [188](file:///home/glemaitre/Documents/packages/scikit-learn/examples/model_selection/plot_tuned_threshold_classifier_with_metadata_routing.py?line=187) # %%
      [189](file:///home/glemaitre/Documents/packages/scikit-learn/examples/model_selection/plot_tuned_threshold_classifier_with_metadata_routing.py?line=188) df = pd.DataFrame({"col_1": ["A", "B", pd.NA]})
----> [190](file:///home/glemaitre/Documents/packages/scikit-learn/examples/model_selection/plot_tuned_threshold_classifier_with_metadata_routing.py?line=189) OneHotEncoder(sparse_output=False).fit_transform(df)

File [~/Documents/packages/scikit-learn/sklearn/utils/_set_output.py:140](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/~/Documents/packages/scikit-learn/sklearn/utils/_set_output.py:140), in _wrap_method_output..wrapped(self, X, *args, **kwargs)
    [138](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_set_output.py?line=137) @wraps(f)
    [139](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_set_output.py?line=138) def wrapped(self, X, *args, **kwargs):
--> [140](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_set_output.py?line=139)     data_to_wrap = f(self, X, *args, **kwargs)
    [141](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_set_output.py?line=140)     if isinstance(data_to_wrap, tuple):
    [142](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_set_output.py?line=141)         # only wrap the first output for cross decomposition
    [143](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_set_output.py?line=142)         return_tuple = (
    [144](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_set_output.py?line=143)             _wrap_data_with_container(method, data_to_wrap[0], X, self),
    [145](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_set_output.py?line=144)             *data_to_wrap[1:],
    [146](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_set_output.py?line=145)         )

File [~/Documents/packages/scikit-learn/sklearn/base.py:948](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/~/Documents/packages/scikit-learn/sklearn/base.py:948), in TransformerMixin.fit_transform(self, X, y, **fit_params)
    [933](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=932)         warnings.warn(
    [934](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=933)             (
    [935](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=934)                 f"This object ({self.__class__.__name__}) has a `transform`"
   (...)
    [943](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=942)             UserWarning,
    [944](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=943)         )
    [946](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=945) if y is None:
    [947](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=946)     # fit method of arity 1 (unsupervised transformation)
--> [948](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=947)     return self.fit(X, **fit_params).transform(X)
    [949](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=948) else:
    [950](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=949)     # fit method of arity 2 (supervised transformation)
    [951](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=950)     return self.fit(X, y, **fit_params).transform(X)

File [~/Documents/packages/scikit-learn/sklearn/base.py:1215](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/~/Documents/packages/scikit-learn/sklearn/base.py:1215), in _fit_context..decorator..wrapper(estimator, *args, **kwargs)
   [1208](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=1207)     estimator._validate_params()
   [1210](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=1209) with config_context(
   [1211](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=1210)     skip_parameter_validation=(
   [1212](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=1211)         prefer_skip_nested_validation or global_skip_validation
   [1213](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=1212)     )
   [1214](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=1213) ):
-> [1215](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/base.py?line=1214)     return fit_method(estimator, *args, **kwargs)

File [~/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py:982](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/~/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py:982), in OneHotEncoder.fit(self, X, y)
    [972](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=971)     warnings.warn(
    [973](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=972)         (
    [974](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=973)             "`sparse` was renamed to `sparse_output` in version 1.2 and "
   (...)
    [978](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=977)         FutureWarning,
    [979](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=978)     )
    [980](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=979)     self.sparse_output = self.sparse
--> [982](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=981) self._fit(
    [983](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=982)     X,
    [984](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=983)     handle_unknown=self.handle_unknown,
    [985](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=984)     force_all_finite="allow-nan",
    [986](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=985) )
    [987](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=986) self._set_drop_idx()
    [988](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=987) self._n_features_outs = self._compute_n_features_outs()

File [~/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py:97](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/~/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py:97), in _BaseEncoder._fit(self, X, handle_unknown, force_all_finite, return_counts, return_and_ignore_missing_for_infrequent)
     [94](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=93) Xi = X_list[i]
     [96](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=95) if self.categories == "auto":
---> [97](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=96)     result = _unique(Xi, return_counts=compute_counts)
     [98](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=97)     if compute_counts:
     [99](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/preprocessing/_encoders.py?line=98)         cats, counts = result

File [~/Documents/packages/scikit-learn/sklearn/utils/_encode.py:42](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/~/Documents/packages/scikit-learn/sklearn/utils/_encode.py:42), in _unique(values, return_inverse, return_counts)
     [11](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=10) """Helper function to find unique values with support for python objects.
     [12](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=11) 
     [13](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=12) Uses pure python method for object dtype, and numpy method for
   (...)
     [39](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=38)     array. Only provided if `return_counts` is True.
     [40](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=39) """
     [41](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=40) if values.dtype == object:
---> [42](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=41)     return _unique_python(
     [43](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=42)         values, return_inverse=return_inverse, return_counts=return_counts
     [44](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=43)     )
     [45](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=44) # numerical
     [46](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=45) return _unique_np(
     [47](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=46)     values, return_inverse=return_inverse, return_counts=return_counts
     [48](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=47) )

File [~/Documents/packages/scikit-learn/sklearn/utils/_encode.py:179](https://file+.vscode-resource.vscode-cdn.net/home/glemaitre/Documents/packages/scikit-learn/~/Documents/packages/scikit-learn/sklearn/utils/_encode.py:179), in _unique_python(values, return_inverse, return_counts)
    [177](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=176) except TypeError:
    [178](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=177)     types = sorted(t.__qualname__ for t in set(type(v) for v in values))
--> [179](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=178)     raise TypeError(
    [180](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=179)         "Encoders require their input to be uniformly "
    [181](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=180)         f"strings or numbers. Got {types}"
    [182](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=181)     )
    [183](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=182) ret = (uniques,)
    [185](file:///home/glemaitre/Documents/packages/scikit-learn/sklearn/utils/_encode.py?line=184) if return_inverse:

TypeError: Encoders require their input to be uniformly strings or numbers. Got ['NAType', 'str']
```

</details>

If `np.nan` is used, then we are fine:

```python
df = pd.DataFrame({"col_1": ["A", "B", np.nan]})
OneHotEncoder(sparse_output=False).fit_transform(df)
```

```
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])
```

```python
df = pd.DataFrame({"col_1": ["A", "B", np.nan]})
OrdinalEncoder().fit_transform(df)
```

```
array([[ 0.],
       [ 1.],
       [nan]])
```

COMMENT:
I think that we need to have a look at this bug. As reported in #29042 it seems that we can get a `pd.NA` with other strings when using Arrow as a backend in pandas. So this is likely to happen in the future.

Labels: Bug, module:preprocessing