Support other dataframes like polars and pyarrow not just pandas

### Describe the workflow you want to enable

Currently, scikit-learn nowhere claims to support [pyarrow](https://arrow.apache.org/docs/python/) or [polars](https://www.pola.rs/). And indeed,
```python
import numpy as np
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler, KBinsDiscretizer
from sklearn.compose import ColumnTransformer

X, y = load_iris(as_frame=True, return_X_y=True)
sepal_cols = ["sepal length (cm)", "sepal width (cm)"]
petal_cols = ["petal length (cm)", "petal width (cm)"]

preprocessor = ColumnTransformer(
    [
        ("scaler", StandardScaler(), sepal_cols),
        ("kbin", KBinsDiscretizer(encode="ordinal"), petal_cols),
    ],
    verbose_feature_names_out=False,
)

import polars as pl  # or import pyarrow as pa
X_pl = pl.from_pandas(X)  # or X_pa = pa.table(X)

preprocessor.fit_transform(X_pl)
# preprocessor.set_output(transform="pandas").fit_transform(X_pl)
```

errors with
```
AttributeError: 'numpy.ndarray' object has no attribute 'columns'

During handling of the above exception, another exception occurred:

ValueError: Specifying the columns using strings is only supported for pandas DataFrames
```

### Describe your proposed solution

scikit-learn should support those dataframes, maybe via the [python dataframe interchange protocol](https://data-apis.org/dataframe-protocol/latest/index.html).

In that regard, a new option like `set_output(transform="dataframe")` would be nice.

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

Some related discussion came up in #25813.

COMMENT:
> FYI, the above code snippet now works, I guess since [#26464](https://github.com/scikit-learn/scikit-learn/pull/26464). So I'm inclined to close.

It works for Polars, but not for PyArrow, right?

At least:
```python
import numpy as np
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler, KBinsDiscretizer
from sklearn.compose import ColumnTransformer

X, y = load_iris(as_frame=True, return_X_y=True)
sepal_cols = ["sepal length (cm)", "sepal width (cm)"]
petal_cols = ["petal length (cm)", "petal width (cm)"]

preprocessor = ColumnTransformer(
    [
        ("scaler", StandardScaler(), sepal_cols),
        ("kbin", KBinsDiscretizer(encode="ordinal"), petal_cols),
    ],
    verbose_feature_names_out=False,
)

import pyarrow as pa
X_pa = pa.table(X)

preprocessor.fit_transform(X_pa)
```
raises
```python-traceback
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[2], line 21
     18 import pyarrow as pa
     19 X_pa = pa.table(X)
---> 21 preprocessor.fit_transform(X_pa)

File ~/scratch/.310venv/lib/python3.10/site-packages/sklearn/utils/_set_output.py:319, in _wrap_method_output.<locals>.wrapped(self, X, *args, **kwargs)
    317 @wraps(f)
    318 def wrapped(self, X, *args, **kwargs):
--> 319     data_to_wrap = f(self, X, *args, **kwargs)
    320     if isinstance(data_to_wrap, tuple):
    321         # only wrap the first output for cross decomposition
    322         return_tuple = (
    323             _wrap_data_with_container(method, data_to_wrap[0], X, self),
    324             *data_to_wrap[1:],
    325         )

File ~/scratch/.310venv/lib/python3.10/site-packages/sklearn/base.py:1389, in _fit_context.<locals>.decorator.<locals>.wrapper(estimator, *args, **kwargs)
   1382     estimator._validate_params()
   1384 with config_context(
   1385     skip_parameter_validation=(
   1386         prefer_skip_nested_validation or global_skip_validation
   1387     )
   1388 ):
-> 1389     return fit_method(estimator, *args, **kwargs)

File ~/scratch/.310venv/lib/python3.10/site-packages/sklearn/compose/_column_transformer.py:1001, in ColumnTransformer.fit_transform(self, X, y, **params)
    998 else:
    999     routed_params = self._get_empty_routing()
-> 1001 result = self._call_func_on_transformers(
   1002     X,
   1003     y,
   1004     _fit_transform_one,
   1005     column_as_labels=False,
   1006     routed_params=routed_params,
   1007 )
   1009 if not result:
   1010     self._update_fitted_transformers([])

File ~/scratch/.310venv/lib/python3.10/site-packages/sklearn/compose/_column_transformer.py:902, in ColumnTransformer._call_func_on_transformers(self, X, y, func, column_as_labels, routed_params)
    897         else:  # func is _transform_one
    898             extra_args = {}
    899         jobs.append(
    900             delayed(func)(
    901                 transformer=clone(trans) if not fitted else trans,
--> 902                 X=_safe_indexing(X, columns, axis=1),
    903                 y=y,
    904                 weight=weight,
    905                 **extra_args,
    906                 params=routed_params[name],
    907             )
    908         )
    910     return Parallel(n_jobs=self.n_jobs)(jobs)
    912 except ValueError as e:

File ~/scratch/.310venv/lib/python3.10/site-packages/sklearn/utils/_indexing.py:270, in _safe_indexing(X, indices, axis)
    268     return _polars_indexing(X, indices, indices_dtype, axis=axis)
    269 elif hasattr(X, "shape"):
--> 270     return _array_indexing(X, indices, indices_dtype, axis=axis)
    271 else:
    272     return _list_indexing(X, indices, indices_dtype)

File ~/scratch/.310venv/lib/python3.10/site-packages/sklearn/utils/_indexing.py:36, in _array_indexing(array, key, key_dtype, axis)
     34 if isinstance(key, tuple):
     35     key = list(key)
---> 36 return array[key, ...] if axis == 0 else array[:, key]

File ~/scratch/.310venv/lib/python3.10/site-packages/pyarrow/table.pxi:1693, in pyarrow.lib._Tabular.__getitem__()

File ~/scratch/.310venv/lib/python3.10/site-packages/pyarrow/table.pxi:1779, in pyarrow.lib._Tabular.column()

File ~/scratch/.310venv/lib/python3.10/site-packages/pyarrow/table.pxi:1725, in pyarrow.lib._Tabular._ensure_integer_index()

TypeError: Index must either be string or integer
```

Given that the original issue also mentioned PyArrow, may I suggest either reopening until PyArrow support is completed, or making a separate issue for PyArrow support?

Just to avoid ambiguity: I'm not requesting that PyArrow be required in scikit-learn (far from it!), but that `pyarrow.Table` be supported in the same way the `polars.DataFrame` is

---

related issue: https://github.com/scikit-learn/scikit-learn/issues/31019

Labels: New Feature, RFC