Make an instance of ColumnTransformer pass the common test

### Describe the bug

Not sure if `check_estimator` is appropriate to use with transformers, but `ColumnTransformer` inherits from `BaseEstimator`, so I'm assuming so.

I have a custom transformer that inherits from `ColumnTransformer`, and I'm trying to use `check_estimator` to make sure I am doing so properly. However, `ColumnTransformer` itself fails on multiple tests; I'm assuming many are because creating a non-trivial `ColumnTransformer` involves specifying required feature names, which seems to go against some of the tests in `check_estimator`.

Is there a different best practice to test a custom subclass of `ColumnTransformer` and/or transformers in general?

### Steps/Code to Reproduce

```python
from sklearn.utils.estimator_checks import parametrize_with_checks
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import Normalizer

ct = ColumnTransformer(
    [
        ("norm1", Normalizer(norm="l1"), [0, 1]),
        ("norm2", Normalizer(norm="l1"), slice(2, 4)),
    ],
    remainder="passthrough",
)


@parametrize_with_checks([ct])
def test_sklearn_checks(estimator, check):
    check(estimator)
```

### Expected Results

All tests pass.

### Actual Results

[Test Results —.pdf](https://github.com/scikit-learn/scikit-learn/files/13285011/Test.Results.pdf)


### Versions

```shell
System:
    python: 3.11.6 | packaged by conda-forge | (main, Oct  3 2023, 10:37:07) [Clang 15.0.7 ]
executable: /my_env/bin/python
   machine: macOS-13.4-arm64-arm-64bit
Python dependencies:
      sklearn: 1.3.1
          pip: 23.3.1
   setuptools: 68.2.2
        numpy: 1.26.0
        scipy: 1.11.3
       Cython: None
       pandas: 2.1.1
   matplotlib: 3.8.0
       joblib: 1.3.2
threadpoolctl: 3.2.0
Built with OpenMP: True
threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 8
         prefix: libopenblas
       filepath: /my_env/lib/libopenblas.0.dylib
        version: 0.3.24
threading_layer: openmp
   architecture: VORTEX
       user_api: openmp
   internal_api: openmp
    num_threads: 8
         prefix: libomp
       filepath: /my_env/lib/libomp.dylib
        version: None
```

COMMENT:
I modified the test to include only one column, and I think we need to fix some of the issues.

```py
from sklearn.utils.estimator_checks import parametrize_with_checks
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import Normalizer

ct = ColumnTransformer(
    [
        ("norm1", Normalizer(norm="l1"), [0]),
    ],
    remainder="passthrough",
)


@parametrize_with_checks([ct])
def test_sklearn_checks(estimator, check):
    check(estimator)
```

results:

<details>

``` trace
============================= test session starts ==============================
platform linux -- Python 3.11.4, pytest-7.3.2, pluggy-1.2.0
rootdir: /tmp
plugins: cov-4.1.0, flaky-3.7.0, anyio-3.7.1
collected 35 items

../../../../../tmp/1.py .......F...F...F.....s...........FF              [100%]

=================================== FAILURES ===================================
_ test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_estimators_empty_data_messages] _

X = array([], shape=(12, 0), dtype=float64), key = [0]

    def _get_column_indices(X, key):
        """Get feature column indices for input data X and key.
    
        For accepted values of `key`, see the docstring of
        :func:`_safe_indexing`.
        """
        n_columns = X.shape[1]
    
        key_dtype = _determine_key_type(key)
    
        if isinstance(key, (list, tuple)) and not key:
            # we get an empty list
            return []
        elif key_dtype in ("bool", "int"):
            # Convert key into positive indexes
            try:
>               idx = _safe_indexing(np.arange(n_columns), key)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/__init__.py:413: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

X = array([], dtype=int64), indices = [0]

    def _safe_indexing(X, indices, *, axis=0):
        """Return rows, items or columns of X using indices.
    
        .. warning::
    
            This utility is documented, but **private**. This means that
            backward compatibility might be broken without any deprecation
            cycle.
    
        Parameters
        ----------
        X : array-like, sparse-matrix, list, pandas.DataFrame, pandas.Series
            Data from which to sample rows, items or columns. `list` are only
            supported when `axis=0`.
        indices : bool, int, str, slice, array-like
            - If `axis=0`, boolean and integer array-like, integer slice,
              and scalar integer are supported.
            - If `axis=1`:
                - to select a single column, `indices` can be of `int` type for
                  all `X` types and `str` only for dataframe. The selected subset
                  will be 1D, unless `X` is a sparse matrix in which case it will
                  be 2D.
                - to select multiples columns, `indices` can be one of the
                  following: `list`, `array`, `slice`. The type used in
                  these containers can be one of the following: `int`, 'bool' and
                  `str`. However, `str` is only supported when `X` is a dataframe.
                  The selected subset will be 2D.
        axis : int, default=0
            The axis along which `X` will be subsampled. `axis=0` will select
            rows while `axis=1` will select columns.
    
        Returns
        -------
        subset
            Subset of X on axis 0 or 1.
    
        Notes
        -----
        CSR, CSC, and LIL sparse matrices are supported. COO sparse matrices are
        not supported.
        """
        if indices is None:
            return X
    
        if axis not in (0, 1):
            raise ValueError(
                "'axis' should be either 0 (to index rows) or 1 (to index "
                " column). Got {} instead.".format(axis)
            )
    
        indices_dtype = _determine_key_type(indices)
    
        if axis == 0 and indices_dtype == "str":
            raise ValueError("String indexing is not supported with 'axis=0'")
    
        if axis == 1 and X.ndim != 2:
            raise ValueError(
                "'X' should be a 2D NumPy array, 2D sparse matrix or pandas "
                "dataframe when indexing the columns (i.e. 'axis=1'). "
                "Got {} instead with {} dimension(s).".format(type(X), X.ndim)
            )
    
        if axis == 1 and indices_dtype == "str" and not hasattr(X, "loc"):
            raise ValueError(
                "Specifying the columns using strings is only supported for "
                "pandas DataFrames"
            )
    
        if hasattr(X, "iloc"):
            return _pandas_indexing(X, indices, indices_dtype, axis=axis)
        elif hasattr(X, "shape"):
>           return _array_indexing(X, indices, indices_dtype, axis=axis)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/__init__.py:355: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

array = array([], dtype=int64), key = [0], key_dtype = 'int', axis = 0

    def _array_indexing(array, key, key_dtype, axis):
        """Index an array or scipy.sparse consistently across NumPy version."""
        if issparse(array) and key_dtype == "bool":
            key = np.asarray(key)
        if isinstance(key, tuple):
            key = list(key)
>       return array[key] if axis == 0 else array[:, key]
E       IndexError: index 0 is out of bounds for axis 0 with size 0

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/__init__.py:184: IndexError

The above exception was the direct cause of the following exception:

name = 'ColumnTransformer'
estimator_orig = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])

    @ignore_warnings(category=FutureWarning)
    def check_estimators_empty_data_messages(name, estimator_orig):
        e = clone(estimator_orig)
        set_random_state(e, 1)
    
        X_zero_samples = np.empty(0).reshape(0, 3)
        # The precise message can change depending on whether X or y is
        # validated first. Let us test the type of exception only:
        err_msg = (
            f"The estimator {name} does not raise a ValueError when an "
            "empty data is used to train. Perhaps use check_array in train."
        )
        with raises(ValueError, err_msg=err_msg):
            e.fit(X_zero_samples, [])
    
        X_zero_features = np.empty(0).reshape(12, 0)
        # the following y should be accepted by both classifiers and regressors
        # and ignored by unsupervised models
        y = _enforce_estimator_tags_y(e, np.array([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]))
        msg = r"0 feature\(s\) \(shape=\(\d*, 0\)\) while a minimum of \d* " "is required."
        with raises(ValueError, match=msg):
>           e.fit(X_zero_features, y)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/estimator_checks.py:1908: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = array([], shape=(12, 0), dtype=float64)
y = array([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0])

    def fit(self, X, y=None):
        """Fit all transformers using X.
    
        Parameters
        ----------
        X : {array-like, dataframe} of shape (n_samples, n_features)
            Input data, of which specified subsets are used to fit the
            transformers.
    
        y : array-like of shape (n_samples,...), default=None
            Targets for supervised learning.
    
        Returns
        -------
        self : ColumnTransformer
            This estimator.
        """
        # we use fit_transform to make sure to set sparse_output_ (for which we
        # need the transformed data) to have consistent output type in predict
>       self.fit_transform(X, y=y)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:717: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = array([], shape=(12, 0), dtype=float64), args = ()
kwargs = {'y': array([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0])}

    @wraps(f)
    def wrapped(self, X, *args, **kwargs):
>       data_to_wrap = f(self, X, *args, **kwargs)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_set_output.py:157: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

estimator = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
args = (array([], shape=(12, 0), dtype=float64),)
kwargs = {'y': array([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0])}
global_skip_validation = False, partial_fit_and_fitted = False

    @functools.wraps(fit_method)
    def wrapper(estimator, *args, **kwargs):
        global_skip_validation = get_config()["skip_parameter_validation"]
    
        # we don't want to validate again for each call to partial_fit
        partial_fit_and_fitted = (
            fit_method.__name__ == "partial_fit" and _is_fitted(estimator)
        )
    
        if not global_skip_validation and not partial_fit_and_fitted:
            estimator._validate_params()
    
        with config_context(
            skip_parameter_validation=(
                prefer_skip_nested_validation or global_skip_validation
            )
        ):
>           return fit_method(estimator, *args, **kwargs)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/base.py:1152: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = array([], shape=(12, 0), dtype=float64)
y = array([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0])

    @_fit_context(
        # estimators in ColumnTransformer.transformers are not validated yet
        prefer_skip_nested_validation=False
    )
    def fit_transform(self, X, y=None):
        """Fit all transformers, transform the data and concatenate results.
    
        Parameters
        ----------
        X : {array-like, dataframe} of shape (n_samples, n_features)
            Input data, of which specified subsets are used to fit the
            transformers.
    
        y : array-like of shape (n_samples,), default=None
            Targets for supervised learning.
    
        Returns
        -------
        X_t : {array-like, sparse matrix} of \
                shape (n_samples, sum_n_components)
            Horizontally stacked results of transformers. sum_n_components is the
            sum of n_components (output dimension) over transformers. If
            any result is a sparse matrix, everything will be converted to
            sparse matrices.
        """
        self._check_feature_names(X, reset=True)
    
        X = _check_X(X)
        # set n_features_in_ attribute
        self._check_n_features(X, reset=True)
        self._validate_transformers()
>       self._validate_column_callables(X)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:751: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = array([], shape=(12, 0), dtype=float64)

    def _validate_column_callables(self, X):
        """
        Converts callable column specifications.
        """
        all_columns = []
        transformer_to_input_indices = {}
        for name, _, columns in self.transformers:
            if callable(columns):
                columns = columns(X)
            all_columns.append(columns)
>           transformer_to_input_indices[name] = _get_column_indices(X, columns)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:459: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

X = array([], shape=(12, 0), dtype=float64), key = [0]

    def _get_column_indices(X, key):
        """Get feature column indices for input data X and key.
    
        For accepted values of `key`, see the docstring of
        :func:`_safe_indexing`.
        """
        n_columns = X.shape[1]
    
        key_dtype = _determine_key_type(key)
    
        if isinstance(key, (list, tuple)) and not key:
            # we get an empty list
            return []
        elif key_dtype in ("bool", "int"):
            # Convert key into positive indexes
            try:
                idx = _safe_indexing(np.arange(n_columns), key)
            except IndexError as e:
>               raise ValueError(
                    "all features must be in [0, {}] or [-{}, 0]".format(
                        n_columns - 1, n_columns
                    )
                ) from e
E               ValueError: all features must be in [0, -1] or [-0, 0]

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/__init__.py:415: ValueError

The above exception was the direct cause of the following exception:

estimator = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
check = functools.partial(<function check_estimators_empty_data_messages at 0x7f49c64722a0>, 'ColumnTransformer')

    @parametrize_with_checks([ct])
    def test_sklearn_checks(estimator, check):
>       check(estimator)

/tmp/1.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_testing.py:156: in wrapper
    return fn(*args, **kwargs)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/estimator_checks.py:1907: in check_estimators_empty_data_messages
    with raises(ValueError, match=msg):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sklearn.utils._testing._Raises object at 0x7f49c64be490>
exc_type = <class 'ValueError'>
exc_value = ValueError('all features must be in [0, -1] or [-0, 0]')
_ = <traceback object at 0x7f49c64c4b40>

    def __exit__(self, exc_type, exc_value, _):
        # see
        # https://docs.python.org/2.5/whatsnew/pep-343.html#SECTION000910000000000000000
    
        if exc_type is None:  # No exception was raised in the block
            if self.may_pass:
                return True  # CM is happy
            else:
                err_msg = self.err_msg or f"Did not raise: {self.expected_exc_types}"
                raise AssertionError(err_msg)
    
        if not any(
            issubclass(exc_type, expected_type)
            for expected_type in self.expected_exc_types
        ):
            if self.err_msg is not None:
                raise AssertionError(self.err_msg) from exc_value
            else:
                return False  # will re-raise the original exception
    
        if self.matches is not None:
            err_msg = self.err_msg or (
                "The error message should contain one of the following "
                "patterns:\n{}\nGot {}".format("\n".join(self.matches), str(exc_value))
            )
            if not any(re.search(match, str(exc_value)) for match in self.matches):
>               raise AssertionError(err_msg) from exc_value
E               AssertionError: The error message should contain one of the following patterns:
E               0 feature\(s\) \(shape=\(\d*, 0\)\) while a minimum of \d* is required.
E               Got all features must be in [0, -1] or [-0, 0]

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_testing.py:907: AssertionError
_ test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_estimator_sparse_data] _

name = 'ColumnTransformer'
estimator_orig = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])

    def check_estimator_sparse_data(name, estimator_orig):
        rng = np.random.RandomState(0)
        X = rng.uniform(size=(40, 3))
        X[X < 0.8] = 0
        X = _enforce_estimator_tags_X(estimator_orig, X)
        X_csr = sparse.csr_matrix(X)
        y = (4 * rng.uniform(size=40)).astype(int)
        # catch deprecation warnings
        with ignore_warnings(category=FutureWarning):
            estimator = clone(estimator_orig)
        y = _enforce_estimator_tags_y(estimator, y)
        tags = _safe_tags(estimator_orig)
        for matrix_format, X in _generate_sparse_matrix(X_csr):
            # catch deprecation warnings
            with ignore_warnings(category=FutureWarning):
                estimator = clone(estimator_orig)
                if name in ["Scaler", "StandardScaler"]:
                    estimator.set_params(with_mean=False)
            # fit and predict
            if "64" in matrix_format:
                err_msg = (
                    f"Estimator {name} doesn't seem to support {matrix_format} "
                    "matrix, and is not failing gracefully, e.g. by using "
                    "check_array(X, accept_large_sparse=False)"
                )
            else:
                err_msg = (
                    f"Estimator {name} doesn't seem to fail gracefully on sparse "
                    "data: error message should state explicitly that sparse "
                    "input is not supported if this is not the case."
                )
            with raises(
                (TypeError, ValueError),
                match=["sparse", "Sparse"],
                may_pass=True,
                err_msg=err_msg,
            ):
                with ignore_warnings(category=FutureWarning):
>                   estimator.fit(X, y)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/estimator_checks.py:991: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = <40x3 sparse matrix of type '<class 'numpy.float64'>'
	with 53 stored elements (18 diagonals) in DIAgonal format>
y = array([2, 2, 3, 2, 1, 2, 0, 1, 2, 1, 2, 1, 0, 1, 2, 2, 2, 2, 2, 1, 3, 1,
       1, 3, 3, 2, 0, 3, 2, 3, 0, 3, 0, 2, 0, 3, 3, 2, 1, 0])

    def fit(self, X, y=None):
        """Fit all transformers using X.
    
        Parameters
        ----------
        X : {array-like, dataframe} of shape (n_samples, n_features)
            Input data, of which specified subsets are used to fit the
            transformers.
    
        y : array-like of shape (n_samples,...), default=None
            Targets for supervised learning.
    
        Returns
        -------
        self : ColumnTransformer
            This estimator.
        """
        # we use fit_transform to make sure to set sparse_output_ (for which we
        # need the transformed data) to have consistent output type in predict
>       self.fit_transform(X, y=y)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:717: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = <40x3 sparse matrix of type '<class 'numpy.float64'>'
	with 53 stored elements (18 diagonals) in DIAgonal format>
args = ()
kwargs = {'y': array([2, 2, 3, 2, 1, 2, 0, 1, 2, 1, 2, 1, 0, 1, 2, 2, 2, 2, 2, 1, 3, 1,
       1, 3, 3, 2, 0, 3, 2, 3, 0, 3, 0, 2, 0, 3, 3, 2, 1, 0])}

    @wraps(f)
    def wrapped(self, X, *args, **kwargs):
>       data_to_wrap = f(self, X, *args, **kwargs)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_set_output.py:157: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

estimator = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
args = (<40x3 sparse matrix of type '<class 'numpy.float64'>'
	with 53 stored elements (18 diagonals) in DIAgonal format>,)
kwargs = {'y': array([2, 2, 3, 2, 1, 2, 0, 1, 2, 1, 2, 1, 0, 1, 2, 2, 2, 2, 2, 1, 3, 1,
       1, 3, 3, 2, 0, 3, 2, 3, 0, 3, 0, 2, 0, 3, 3, 2, 1, 0])}
global_skip_validation = False, partial_fit_and_fitted = False

    @functools.wraps(fit_method)
    def wrapper(estimator, *args, **kwargs):
        global_skip_validation = get_config()["skip_parameter_validation"]
    
        # we don't want to validate again for each call to partial_fit
        partial_fit_and_fitted = (
            fit_method.__name__ == "partial_fit" and _is_fitted(estimator)
        )
    
        if not global_skip_validation and not partial_fit_and_fitted:
            estimator._validate_params()
    
        with config_context(
            skip_parameter_validation=(
                prefer_skip_nested_validation or global_skip_validation
            )
        ):
>           return fit_method(estimator, *args, **kwargs)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/base.py:1152: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = <40x3 sparse matrix of type '<class 'numpy.float64'>'
	with 53 stored elements (18 diagonals) in DIAgonal format>
y = array([2, 2, 3, 2, 1, 2, 0, 1, 2, 1, 2, 1, 0, 1, 2, 2, 2, 2, 2, 1, 3, 1,
       1, 3, 3, 2, 0, 3, 2, 3, 0, 3, 0, 2, 0, 3, 3, 2, 1, 0])

    @_fit_context(
        # estimators in ColumnTransformer.transformers are not validated yet
        prefer_skip_nested_validation=False
    )
    def fit_transform(self, X, y=None):
        """Fit all transformers, transform the data and concatenate results.
    
        Parameters
        ----------
        X : {array-like, dataframe} of shape (n_samples, n_features)
            Input data, of which specified subsets are used to fit the
            transformers.
    
        y : array-like of shape (n_samples,), default=None
            Targets for supervised learning.
    
        Returns
        -------
        X_t : {array-like, sparse matrix} of \
                shape (n_samples, sum_n_components)
            Horizontally stacked results of transformers. sum_n_components is the
            sum of n_components (output dimension) over transformers. If
            any result is a sparse matrix, everything will be converted to
            sparse matrices.
        """
        self._check_feature_names(X, reset=True)
    
        X = _check_X(X)
        # set n_features_in_ attribute
        self._check_n_features(X, reset=True)
        self._validate_transformers()
        self._validate_column_callables(X)
        self._validate_remainder(X)
    
>       result = self._fit_transform(X, y, _fit_transform_one)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:754: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = <40x3 sparse matrix of type '<class 'numpy.float64'>'
	with 53 stored elements (18 diagonals) in DIAgonal format>
y = array([2, 2, 3, 2, 1, 2, 0, 1, 2, 1, 2, 1, 0, 1, 2, 2, 2, 2, 2, 1, 3, 1,
       1, 3, 3, 2, 0, 3, 2, 3, 0, 3, 0, 2, 0, 3, 3, 2, 1, 0])
func = <function _fit_transform_one at 0x7f49c6780180>, fitted = False
column_as_strings = False

    def _fit_transform(self, X, y, func, fitted=False, column_as_strings=False):
        """
        Private function to fit and/or transform on demand.
    
        Return value (transformers and/or transformed X data) depends
        on the passed function.
        ``fitted=True`` ensures the fitted transformers are used.
        """
        transformers = list(
            self._iter(
                fitted=fitted, replace_strings=True, column_as_strings=column_as_strings
            )
        )
        try:
>           return Parallel(n_jobs=self.n_jobs)(
                delayed(func)(
                    transformer=clone(trans) if not fitted else trans,
                    X=_safe_indexing(X, column, axis=1),
                    y=y,
                    weight=weight,
                    message_clsname="ColumnTransformer",
                    message=self._log_message(name, idx, len(transformers)),
                )
                for idx, (name, trans, column, weight) in enumerate(transformers, 1)
            )

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:681: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Parallel(n_jobs=1)
iterable = <generator object ColumnTransformer._fit_transform.<locals>.<genexpr> at 0x7f49c653c040>

    def __call__(self, iterable):
        """Dispatch the tasks and return the results.
    
        Parameters
        ----------
        iterable : iterable
            Iterable containing tuples of (delayed_function, args, kwargs) that should
            be consumed.
    
        Returns
        -------
        results : list
            List of results of the tasks.
        """
        # Capture the thread-local scikit-learn configuration at the time
        # Parallel.__call__ is issued since the tasks can be dispatched
        # in a different thread depending on the backend and on the value of
        # pre_dispatch and n_jobs.
        config = get_config()
        iterable_with_config = (
            (_with_config(delayed_func, config), args, kwargs)
            for delayed_func, args, kwargs in iterable
        )
>       return super().__call__(iterable_with_config)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/parallel.py:65: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Parallel(n_jobs=1)
iterable = <generator object Parallel.__call__.<locals>.<genexpr> at 0x7f49c64b4340>

    def __call__(self, iterable):
        """Main function to dispatch parallel tasks."""
    
        self._reset_run_tracking()
        self._start_time = time.time()
    
        if not self._managed_backend:
            n_jobs = self._initialize_backend()
        else:
            n_jobs = self._effective_n_jobs()
    
        if n_jobs == 1:
            # If n_jobs==1, run the computation sequentially and return
            # immediatly to avoid overheads.
            output = self._get_sequential_output(iterable)
            next(output)
>           return output if self.return_generator else list(output)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/joblib/parallel.py:1863: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Parallel(n_jobs=1)
iterable = <generator object Parallel.__call__.<locals>.<genexpr> at 0x7f49c64b4340>

    def _get_sequential_output(self, iterable):
        """Separate loop for sequential output.
    
        This simplifies the traceback in case of errors and reduces the
        overhead of calling sequential tasks with `joblib`.
        """
        try:
            self._iterating = True
            self._original_iterator = iterable
            batch_size = self._get_batch_size()
    
            if batch_size != 1:
                it = iter(iterable)
                iterable_batched = iter(
                    lambda: tuple(itertools.islice(it, batch_size)), ()
                )
                iterable = (
                    task for batch in iterable_batched for task in batch
                )
    
            # first yield returns None, for internal use only. This ensures
            # that we enter the try/except block and setup the generator.
            yield None
    
            # Sequentially call the tasks and yield the results.
>           for func, args, kwargs in iterable:

../../../miniforge3/envs/skops/lib/python3.11/site-packages/joblib/parallel.py:1789: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

.0 = <generator object ColumnTransformer._fit_transform.<locals>.<genexpr> at 0x7f49c653c040>

>   iterable_with_config = (
        (_with_config(delayed_func, config), args, kwargs)
        for delayed_func, args, kwargs in iterable
    )

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/parallel.py:61: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

.0 = <enumerate object at 0x7f49c614acf0>

    return Parallel(n_jobs=self.n_jobs)(
        delayed(func)(
            transformer=clone(trans) if not fitted else trans,
>           X=_safe_indexing(X, column, axis=1),
            y=y,
            weight=weight,
            message_clsname="ColumnTransformer",
            message=self._log_message(name, idx, len(transformers)),
        )
        for idx, (name, trans, column, weight) in enumerate(transformers, 1)
    )

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:684: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

X = <40x3 sparse matrix of type '<class 'numpy.float64'>'
	with 53 stored elements (18 diagonals) in DIAgonal format>
indices = [0]

    def _safe_indexing(X, indices, *, axis=0):
        """Return rows, items or columns of X using indices.
    
        .. warning::
    
            This utility is documented, but **private**. This means that
            backward compatibility might be broken without any deprecation
            cycle.
    
        Parameters
        ----------
        X : array-like, sparse-matrix, list, pandas.DataFrame, pandas.Series
            Data from which to sample rows, items or columns. `list` are only
            supported when `axis=0`.
        indices : bool, int, str, slice, array-like
            - If `axis=0`, boolean and integer array-like, integer slice,
              and scalar integer are supported.
            - If `axis=1`:
                - to select a single column, `indices` can be of `int` type for
                  all `X` types and `str` only for dataframe. The selected subset
                  will be 1D, unless `X` is a sparse matrix in which case it will
                  be 2D.
                - to select multiples columns, `indices` can be one of the
                  following: `list`, `array`, `slice`. The type used in
                  these containers can be one of the following: `int`, 'bool' and
                  `str`. However, `str` is only supported when `X` is a dataframe.
                  The selected subset will be 2D.
        axis : int, default=0
            The axis along which `X` will be subsampled. `axis=0` will select
            rows while `axis=1` will select columns.
    
        Returns
        -------
        subset
            Subset of X on axis 0 or 1.
    
        Notes
        -----
        CSR, CSC, and LIL sparse matrices are supported. COO sparse matrices are
        not supported.
        """
        if indices is None:
            return X
    
        if axis not in (0, 1):
            raise ValueError(
                "'axis' should be either 0 (to index rows) or 1 (to index "
                " column). Got {} instead.".format(axis)
            )
    
        indices_dtype = _determine_key_type(indices)
    
        if axis == 0 and indices_dtype == "str":
            raise ValueError("String indexing is not supported with 'axis=0'")
    
        if axis == 1 and X.ndim != 2:
            raise ValueError(
                "'X' should be a 2D NumPy array, 2D sparse matrix or pandas "
                "dataframe when indexing the columns (i.e. 'axis=1'). "
                "Got {} instead with {} dimension(s).".format(type(X), X.ndim)
            )
    
        if axis == 1 and indices_dtype == "str" and not hasattr(X, "loc"):
            raise ValueError(
                "Specifying the columns using strings is only supported for "
                "pandas DataFrames"
            )
    
        if hasattr(X, "iloc"):
            return _pandas_indexing(X, indices, indices_dtype, axis=axis)
        elif hasattr(X, "shape"):
>           return _array_indexing(X, indices, indices_dtype, axis=axis)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/__init__.py:355: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

array = <40x3 sparse matrix of type '<class 'numpy.float64'>'
	with 53 stored elements (18 diagonals) in DIAgonal format>
key = [0], key_dtype = 'int', axis = 1

    def _array_indexing(array, key, key_dtype, axis):
        """Index an array or scipy.sparse consistently across NumPy version."""
        if issparse(array) and key_dtype == "bool":
            key = np.asarray(key)
        if isinstance(key, tuple):
            key = list(key)
>       return array[key] if axis == 0 else array[:, key]
E       TypeError: 'dia_matrix' object is not subscriptable

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/__init__.py:184: TypeError

The above exception was the direct cause of the following exception:

estimator = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
check = functools.partial(<function check_estimator_sparse_data at 0x7f49c6470220>, 'ColumnTransformer')

    @parametrize_with_checks([ct])
    def test_sklearn_checks(estimator, check):
>       check(estimator)

/tmp/1.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/estimator_checks.py:984: in check_estimator_sparse_data
    with raises(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sklearn.utils._testing._Raises object at 0x7f49c6452650>
exc_type = <class 'TypeError'>
exc_value = TypeError("'dia_matrix' object is not subscriptable")
_ = <traceback object at 0x7f49c62d1000>

    def __exit__(self, exc_type, exc_value, _):
        # see
        # https://docs.python.org/2.5/whatsnew/pep-343.html#SECTION000910000000000000000
    
        if exc_type is None:  # No exception was raised in the block
            if self.may_pass:
                return True  # CM is happy
            else:
                err_msg = self.err_msg or f"Did not raise: {self.expected_exc_types}"
                raise AssertionError(err_msg)
    
        if not any(
            issubclass(exc_type, expected_type)
            for expected_type in self.expected_exc_types
        ):
            if self.err_msg is not None:
                raise AssertionError(self.err_msg) from exc_value
            else:
                return False  # will re-raise the original exception
    
        if self.matches is not None:
            err_msg = self.err_msg or (
                "The error message should contain one of the following "
                "patterns:\n{}\nGot {}".format("\n".join(self.matches), str(exc_value))
            )
            if not any(re.search(match, str(exc_value)) for match in self.matches):
>               raise AssertionError(err_msg) from exc_value
E               AssertionError: Estimator ColumnTransformer doesn't seem to fail gracefully on sparse data: error message should state explicitly that sparse input is not supported if this is not the case.

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_testing.py:907: AssertionError
_ test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_transformer_data_not_an_array] _

estimator = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
check = functools.partial(<function check_transformer_data_not_an_array at 0x7f49c64719e0>, 'ColumnTransformer')

    @parametrize_with_checks([ct])
    def test_sklearn_checks(estimator, check):
>       check(estimator)

/tmp/1.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_testing.py:156: in wrapper
    return fn(*args, **kwargs)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/estimator_checks.py:1641: in check_transformer_data_not_an_array
    _check_transformer(name, transformer, this_X, this_y)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/estimator_checks.py:1693: in _check_transformer
    transformer.fit(X, y_)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:717: in fit
    self.fit_transform(X, y=y)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_set_output.py:157: in wrapped
    data_to_wrap = f(self, X, *args, **kwargs)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/base.py:1152: in wrapper
    return fit_method(estimator, *args, **kwargs)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:751: in fit_transform
    self._validate_column_callables(X)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:459: in _validate_column_callables
    transformer_to_input_indices[name] = _get_column_indices(X, columns)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

X = <sklearn.utils.estimator_checks._NotAnArray object at 0x7f49c5e25450>
key = [0]

    def _get_column_indices(X, key):
        """Get feature column indices for input data X and key.
    
        For accepted values of `key`, see the docstring of
        :func:`_safe_indexing`.
        """
>       n_columns = X.shape[1]
E       AttributeError: '_NotAnArray' object has no attribute 'shape'

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/__init__.py:403: AttributeError
_ test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_fit1d] _

estimator = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
check = functools.partial(<function check_fit1d at 0x7f49c6471760>, 'ColumnTransformer')

    @parametrize_with_checks([ct])
    def test_sklearn_checks(estimator, check):
>       check(estimator)

/tmp/1.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_testing.py:156: in wrapper
    return fn(*args, **kwargs)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/estimator_checks.py:1607: in check_fit1d
    estimator.fit(X, y)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:717: in fit
    self.fit_transform(X, y=y)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_set_output.py:157: in wrapped
    data_to_wrap = f(self, X, *args, **kwargs)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/base.py:1152: in wrapper
    return fit_method(estimator, *args, **kwargs)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:751: in fit_transform
    self._validate_column_callables(X)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:459: in _validate_column_callables
    transformer_to_input_indices[name] = _get_column_indices(X, columns)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

X = array([1.64644051, 2.1455681 , 1.80829013, 1.63464955, 1.2709644 ,
       1.93768234, 1.31276163, 2.675319  , 2.890988...11, 1.58668476, 1.70413368, 2.77678991, 0.21310817,
       0.2613879 , 0.06065519, 2.49785954, 2.33447025, 2.61003644])
key = [0]

    def _get_column_indices(X, key):
        """Get feature column indices for input data X and key.
    
        For accepted values of `key`, see the docstring of
        :func:`_safe_indexing`.
        """
>       n_columns = X.shape[1]
E       IndexError: tuple index out of range

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/__init__.py:403: IndexError
_ test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_fit2d_predict1d] _

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = array([1.64644051, 2.1455681 , 1.80829013]), reset = False

    def _check_n_features(self, X, reset):
        """Set the `n_features_in_` attribute, or check against it.
    
        Parameters
        ----------
        X : {ndarray, sparse matrix} of shape (n_samples, n_features)
            The input samples.
        reset : bool
            If True, the `n_features_in_` attribute is set to `X.shape[1]`.
            If False and the attribute exists, then check that it is equal to
            `X.shape[1]`. If False and the attribute does *not* exist, then
            the check is skipped.
            .. note::
               It is recommended to call reset=True in `fit` and in the first
               call to `partial_fit`. All other methods that validate `X`
               should set `reset=False`.
        """
        try:
>           n_features = _num_features(X)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/base.py:392: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

X = array([1.64644051, 2.1455681 , 1.80829013])

    def _num_features(X):
        """Return the number of features in an array-like X.
    
        This helper function tries hard to avoid to materialize an array version
        of X unless necessary. For instance, if X is a list of lists,
        this function will return the length of the first element, assuming
        that subsequent elements are all lists of the same length without
        checking.
        Parameters
        ----------
        X : array-like
            array-like to get the number of features.
    
        Returns
        -------
        features : int
            Number of features
        """
        type_ = type(X)
        if type_.__module__ == "builtins":
            type_name = type_.__qualname__
        else:
            type_name = f"{type_.__module__}.{type_.__qualname__}"
        message = f"Unable to find the number of features from X of type {type_name}"
        if not hasattr(X, "__len__") and not hasattr(X, "shape"):
            if not hasattr(X, "__array__"):
                raise TypeError(message)
            # Only convert X to a numpy array if there is no cheaper, heuristic
            # option.
            X = np.asarray(X)
    
        if hasattr(X, "shape"):
            if not hasattr(X.shape, "__len__") or len(X.shape) <= 1:
                message += f" with shape {X.shape}"
>               raise TypeError(message)
E               TypeError: Unable to find the number of features from X of type numpy.ndarray with shape (3,)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/validation.py:311: TypeError

The above exception was the direct cause of the following exception:

exceptions = <class 'ValueError'>, message = 'Reshape your data'
function = <bound method ColumnTransformer.transform of ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])>
args = (array([1.64644051, 2.1455681 , 1.80829013]),), kwargs = {}
error_message = 'X does not contain any features, but ColumnTransformer is expecting 3 features'

    def assert_raise_message(exceptions, message, function, *args, **kwargs):
        """Helper function to test the message raised in an exception.
    
        Given an exception, a callable to raise the exception, and
        a message string, tests that the correct exception is raised and
        that the message is a substring of the error thrown. Used to test
        that the specific message thrown during an exception is correct.
    
        Parameters
        ----------
        exceptions : exception or tuple of exception
            An Exception object.
    
        message : str
            The error message or a substring of the error message.
    
        function : callable
            Callable object to raise error.
    
        *args : the positional arguments to `function`.
    
        **kwargs : the keyword arguments to `function`.
        """
        try:
>           function(*args, **kwargs)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_testing.py:210: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = array([1.64644051, 2.1455681 , 1.80829013]), args = (), kwargs = {}

    @wraps(f)
    def wrapped(self, X, *args, **kwargs):
>       data_to_wrap = f(self, X, *args, **kwargs)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_set_output.py:157: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = array([1.64644051, 2.1455681 , 1.80829013])

    def transform(self, X):
        """Transform X separately by each transformer, concatenate results.
    
        Parameters
        ----------
        X : {array-like, dataframe} of shape (n_samples, n_features)
            The data to be transformed by subset.
    
        Returns
        -------
        X_t : {array-like, sparse matrix} of \
                shape (n_samples, sum_n_components)
            Horizontally stacked results of transformers. sum_n_components is the
            sum of n_components (output dimension) over transformers. If
            any result is a sparse matrix, everything will be converted to
            sparse matrices.
        """
        check_is_fitted(self)
        X = _check_X(X)
    
        fit_dataframe_and_transform_dataframe = hasattr(
            self, "feature_names_in_"
        ) and hasattr(X, "columns")
    
        if fit_dataframe_and_transform_dataframe:
            named_transformers = self.named_transformers_
            # check that all names seen in fit are in transform, unless
            # they were dropped
            non_dropped_indices = [
                ind
                for name, ind in self._transformer_to_input_indices.items()
                if name in named_transformers
                and isinstance(named_transformers[name], str)
                and named_transformers[name] != "drop"
            ]
    
            all_indices = set(chain(*non_dropped_indices))
            all_names = set(self.feature_names_in_[ind] for ind in all_indices)
    
            diff = all_names - set(X.columns)
            if diff:
                raise ValueError(f"columns are missing: {diff}")
        else:
            # ndarray was used for fitting or transforming, thus we only
            # check that n_features_in_ is consistent
>           self._check_n_features(X, reset=False)

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/compose/_column_transformer.py:825: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
X = array([1.64644051, 2.1455681 , 1.80829013]), reset = False

    def _check_n_features(self, X, reset):
        """Set the `n_features_in_` attribute, or check against it.
    
        Parameters
        ----------
        X : {ndarray, sparse matrix} of shape (n_samples, n_features)
            The input samples.
        reset : bool
            If True, the `n_features_in_` attribute is set to `X.shape[1]`.
            If False and the attribute exists, then check that it is equal to
            `X.shape[1]`. If False and the attribute does *not* exist, then
            the check is skipped.
            .. note::
               It is recommended to call reset=True in `fit` and in the first
               call to `partial_fit`. All other methods that validate `X`
               should set `reset=False`.
        """
        try:
            n_features = _num_features(X)
        except TypeError as e:
            if not reset and hasattr(self, "n_features_in_"):
>               raise ValueError(
                    "X does not contain any features, but "
                    f"{self.__class__.__name__} is expecting "
                    f"{self.n_features_in_} features"
                ) from e
E               ValueError: X does not contain any features, but ColumnTransformer is expecting 3 features

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/base.py:395: ValueError

During handling of the above exception, another exception occurred:

estimator = ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])
check = functools.partial(<function check_fit2d_predict1d at 0x7f49c6470f40>, 'ColumnTransformer')

    @parametrize_with_checks([ct])
    def test_sklearn_checks(estimator, check):
>       check(estimator)

/tmp/1.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_testing.py:156: in wrapper
    return fn(*args, **kwargs)
../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/estimator_checks.py:1415: in check_fit2d_predict1d
    assert_raise_message(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

exceptions = <class 'ValueError'>, message = 'Reshape your data'
function = <bound method ColumnTransformer.transform of ColumnTransformer(remainder='passthrough',
                  transformers=[('norm1', Normalizer(norm='l1'), [0])])>
args = (array([1.64644051, 2.1455681 , 1.80829013]),), kwargs = {}
error_message = 'X does not contain any features, but ColumnTransformer is expecting 3 features'

    def assert_raise_message(exceptions, message, function, *args, **kwargs):
        """Helper function to test the message raised in an exception.
    
        Given an exception, a callable to raise the exception, and
        a message string, tests that the correct exception is raised and
        that the message is a substring of the error thrown. Used to test
        that the specific message thrown during an exception is correct.
    
        Parameters
        ----------
        exceptions : exception or tuple of exception
            An Exception object.
    
        message : str
            The error message or a substring of the error message.
    
        function : callable
            Callable object to raise error.
    
        *args : the positional arguments to `function`.
    
        **kwargs : the keyword arguments to `function`.
        """
        try:
            function(*args, **kwargs)
        except exceptions as e:
            error_message = str(e)
            if message not in error_message:
>               raise AssertionError(
                    "Error message does not include the expected"
                    " string: %r. Observed error message: %r" % (message, error_message)
                )
E               AssertionError: Error message does not include the expected string: 'Reshape your data'. Observed error message: 'X does not contain any features, but ColumnTransformer is expecting 3 features'

../../../miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/_testing.py:214: AssertionError
=============================== warnings summary ===============================
1.py::test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_parameters_default_constructible]
  /home/adrin/miniforge3/envs/skops/lib/python3.11/site-packages/sklearn/utils/estimator_checks.py:444: SkipTestWarning: Can't instantiate estimator ColumnTransformer parameters ['transformers']
    warnings.warn(msg, SkipTestWarning)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED ../../../../../tmp/1.py::test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_estimators_empty_data_messages]
FAILED ../../../../../tmp/1.py::test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_estimator_sparse_data]
FAILED ../../../../../tmp/1.py::test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_transformer_data_not_an_array]
FAILED ../../../../../tmp/1.py::test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_fit1d]
FAILED ../../../../../tmp/1.py::test_sklearn_checks[ColumnTransformer(remainder='passthrough',transformers=[('norm1',Normalizer(norm='l1'),[0])])-check_fit2d_predict1d]
============== 5 failed, 29 passed, 1 skipped, 1 warning in 2.09s ==============
```
</details>

@glemaitre would you agree we could have them fixed?

Labels: Enhancement