cross validation with Sklearn pipeline using custom data preprocessing

### Describe the bug

I am using this custom sklearn pipeline:
```python
import numpy as np
import pandas as pd
from sklearn.model_selection import cross_validate
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import KFold, StratifiedKFold


class MisCare(BaseEstimator, TransformerMixin):
    def __init__(self, missing_threshold):
        self.missing_threshold = missing_threshold
    def fit(self, X, y=None):
        self.columns_with_gap_ = X.columns[X.isna().sum() > int(self.missing_threshold*X.shape[0])]
        self.mode_ = X.mode().loc[0, :]
        return self
    def transform(self, X, y=None):
        tmp = X.copy()
        tmp.loc[:, self.columns_with_gap_] = tmp.loc[:, self.columns_with_gap_].fillna('GAP')
        tmp.fillna(self.mode_, inplace=True)
        return tmp
    def get_columns_with_gap(self):
        return self.columns_with_gap_
    def get_columns_mode(self):
        return self.mode_


class ConstantCare(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        # Store the columns with non-constant values during the fitting stage
        self.columns_to_keep_ = X.loc[:, X.nunique() != 1].columns
        return self
    
    def transform(self, X, y=None):
        # Keep only the non-constant columns in the input data
        return X.loc[:, self.columns_to_keep_]
    
    def get_columns_to_keep(self):
        return self.columns_to_keep_

    
class CustomOneHotEncoder(OneHotEncoder):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
    
     # these print statements  are check points
    def transform(self, X, y=None):
        print('ohe before:', X.shape)
        transformed_X = super().transform(X.loc[:, self.feature_names_in_])
        print('transformed_X:', transformed_X.shape)
        feature_names = self.get_feature_names_out()
        print('feature_names length:', len(feature_names))
        print('feature_names length (unique):', len(set(feature_names)))
        new_X = pd.DataFrame(transformed_X, columns=feature_names)
        print('ohe after:', new_X.shape)
        return new_X

custom_pipeline = Pipeline(steps=[
    ('mc', MisCare(missing_threshold=0.1)),
    ('cc', ConstantCare()),
    ('one_hot', CustomOneHotEncoder(handle_unknown='infrequent_if_exist', sparse_output=False, drop='first'))
])
```
If I use it in a single run or a custom k-fold validation in a for loop, it runs with no error, but if I use built-in `cross validation` functions, it raises errors.


### Steps/Code to Reproduce

```python
from sklearn.model_selection import cross_val_score, cross_validate

# creating sample data:
tr = pd.DataFrame({'A': np.random.choice(['C','G'], size = 150),
                   'AA': np.random.choice(['C','T'], size = 150),
                   'B': np.random.choice(['C'], size = 150)})
te = pd.DataFrame({'A': np.random.choice(['C','G','T'], size = 5),
                   'AA': np.random.choice(['C','T'], size = 5),
                   'B': np.random.choice(['G','T'], size = 5)})

tr.loc[1,['A', 'AA']] = 'T'
tr.loc[0,'A'] = np.nan
tr.loc[[0,1],'AA'] = ['T','G']
tr.loc[[3,4,5,6,7,8,9,10],'AA'] = np.nan
te.loc[[0,2,3,4],['A', 'AA']] = np.nan

for i in range(50):
    tr.loc[:,'AA_'+str(i)] = tr.loc[:,'AA']
    te.loc[:,'AA_'+str(i)] = te.loc[:,'AA']
    ind = np.random.randint(0,150, np.random.randint(1, 150))
    tr.loc[ind,'AA_'+str(i)] = np.random.choice(['T', 'C'], size = 1)[0]

y = np.random.randint(low =0, high=2, size = 150)
y = np.where(tr['AA']=='T', 1, y)

custom_pipeline = Pipeline(steps=[
    ('mc', MisCare(missing_threshold=0.1)),
    ('cc', ConstantCare()),
    ('one_hot', CustomOneHotEncoder(handle_unknown='infrequent_if_exist', sparse_output=False, drop='first')),
    ('lr', LogisticRegression())
])

# this will raise error !!!!
cv_results = cross_validate(estimator=custom_pipeline, X=tr, y=y, cv=4, return_train_score=True)
print(cv_results)
```

> CC before: (112, 53)
> ohe before: (112, 52)
> transformed_X: (112, 104)
> feature_names length: 104
> feature_names length (unique): 104
> CC before: (112, 53)
> ohe before: (112, 52)
> transformed_X: (112, 140)
> feature_names length: 140
> feature_names length (unique): 140
> CC before: (113, 53)
> ohe before: (113, 52)
> transformed_X: (113, 140)
> feature_names length: 140
> feature_names length (unique): 140
> CC before: (113, 53)
> ohe before: (113, 52)
> transformed_X: (113, 140)
> feature_names length: 140
> feature_names length (unique): 140

As it can be seen, the number of columns are twice the number of inputs, which means that it fails to `drop='first'`. However, with single run, or using for loop for cross validation, this does not happen.



### Expected Results

```python
# creating sample data:
tr = pd.DataFrame({'A': np.random.choice(['C','G'], size = 150),
                   'AA': np.random.choice(['C','T'], size = 150),
                   'B': np.random.choice(['C'], size = 150)})
te = pd.DataFrame({'A': np.random.choice(['C','G','T'], size = 5),
                   'AA': np.random.choice(['C','T'], size = 5),
                   'B': np.random.choice(['G','T'], size = 5)})

tr.loc[1,['A', 'AA']] = 'T'
tr.loc[0,'A'] = np.nan
tr.loc[[0,1],'AA'] = ['T','G']
tr.loc[[3,4,5,6,7,8,9,10],'AA'] = np.nan
te.loc[[0,2,3,4],['A', 'AA']] = np.nan

for i in range(50):
    tr.loc[:,'AA_'+str(i)] = tr.loc[:,'AA']
    te.loc[:,'AA_'+str(i)] = te.loc[:,'AA']
    ind = np.random.randint(0,150, np.random.randint(1, 150))
    tr.loc[ind,'AA_'+str(i)] = np.random.choice(['T', 'C'], size = 1)[0]

y = np.random.randint(low =0, high=2, size = 150)
y = np.where(tr['AA']=='T', 1, y)

```

```python
# a sample run on the train and test data
transformed_tr = custom_pipeline.fit_transform(tr, y)
transformed_te = custom_pipeline.transform(te)
all(transformed_te.columns==transformed_tr)
```

> CC before: (150, 53)
> ohe before: (150, 52)
> transformed_X: (150, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (150, 88)
> CC before: (5, 53)
> ohe before: (5, 52)
> transformed_X: (5, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (5, 88)
> True

```python
# cross validation using for loop
X = tr.copy()
kf = StratifiedKFold(n_splits=5)

custom_pipeline = Pipeline(steps=[
    ('mc', MisCare(missing_threshold=0.1)),
    ('cc', ConstantCare()),
    ('one_hot', CustomOneHotEncoder(handle_unknown='infrequent_if_exist', sparse_output=False, drop='first')),
    ('lr', LogisticRegression())
])

sc = []
for train_index, test_index in kf.split(X,y):
    X_train, X_test = X.loc[train_index,: ], X.loc[test_index,:]
    y_train, y_test = y[train_index], y[test_index]

    # Perform one-hot encoding on X_train and X_test
    encoder = custom_pipeline
    X_train_encoded = encoder.fit(X_train, y_train)
    pred = encoder.predict(X_test)
    sc.append((pred==y_test).mean())
print(sc)
```

> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 52)
> feature_names length: 52
> feature_names length (unique): 52
> ohe after: (120, 52)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 52)
> feature_names length: 52
> feature_names length (unique): 52
> ohe after: (30, 52)
> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (120, 88)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (30, 88)
> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (120, 88)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (30, 88)
> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (120, 88)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (30, 88)
> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (120, 88)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (30, 88)
> [0.7333333333333333, 0.8, 0.7, 0.6666666666666666, 0.7]

Also if use this `ClassifierMixin` class, there won't be any errors:
```python
from sklearn.base import BaseEstimator, ClassifierMixin
class CustomPipeline(BaseEstimator, ClassifierMixin):
    def __init__(self, missing_threshold=0.1):
        self.missing_threshold = missing_threshold
        self.pipeline = Pipeline([
            ('mc', MisCare(missing_threshold=self.missing_threshold)),
            ('cc', ConstantCare()),
            ('one_hot', CustomOneHotEncoder(handle_unknown='infrequent_if_exist',
                                            sparse_output=False, drop='first')),
            ('lr', LogisticRegression())
        ])
        
    def fit(self, X, y=None):
        self.pipeline.fit(X, y)
        return self
    
    def predict(self, X):
        return self.pipeline.predict(X)
    
    def predict_proba(self, X):
        return self.pipeline.predict_proba(X)
    def get_pipeline(self):
        return self.pipeline

custom_pipeline = CustomPipeline(missing_threshold=0.1)
scoring = {'acc': 'accuracy'}
cv_results = cross_validate(custom_pipeline, tr, y, cv=5, return_estimator=True, scoring=scoring)
```

> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 52)
> feature_names length: 52
> feature_names length (unique): 52
> ohe after: (120, 52)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 52)
> feature_names length: 52
> feature_names length (unique): 52
> ohe after: (30, 52)
> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (120, 88)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (30, 88)
> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (120, 88)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (30, 88)
> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (120, 88)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (30, 88)
> CC before: (120, 53)
> ohe before: (120, 52)
> transformed_X: (120, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (120, 88)
> CC before: (30, 53)
> ohe before: (30, 52)
> transformed_X: (30, 88)
> feature_names length: 88
> feature_names length (unique): 88
> ohe after: (30, 88)
```python
cv_results
```

> {'fit_time': array([0.06544304, 0.04276776, 0.04277396, 0.04253197, 0.04913998]),
>  'score_time': array([0.02181005, 0.01875615, 0.01856589, 0.01874685, 0.01908708]),
>  'estimator': [CustomPipeline(),
>   CustomPipeline(),
>   CustomPipeline(),
>   CustomPipeline(),
>   CustomPipeline()],
>  'test_acc': array([0.73333333, 0.8       , 0.7       , 0.66666667, 0.7       ])}

### Actual Results

```python
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [297], in <cell line: 7>()
      1 custom_pipeline = Pipeline(steps=[
      2     ('mc', MisCare(missing_threshold=0.1)),
      3     ('cc', ConstantCare()),
      4     ('one_hot', CustomOneHotEncoder(handle_unknown='infrequent_if_exist', sparse_output=False, drop='first')),
      5     ('lr', LogisticRegression())
      6 ])
----> 7 cv_results = cross_validate(estimator=custom_pipeline, X=tr, y=y, cv=4, return_train_score=True)
      8 print(cv_results)

File ~/opt/anaconda3/lib/python3.9/site-packages/sklearn/model_selection/_validation.py:285, in cross_validate(estimator, X, y, groups, scoring, cv, n_jobs, verbose, fit_params, pre_dispatch, return_train_score, return_estimator, error_score)
    265 parallel = Parallel(n_jobs=n_jobs, verbose=verbose, pre_dispatch=pre_dispatch)
    266 results = parallel(
    267     delayed(_fit_and_score)(
    268         clone(estimator),
   (...)
    282     for train, test in cv.split(X, y, groups)
    283 )
--> 285 _warn_or_raise_about_fit_failures(results, error_score)
    287 # For callabe scoring, the return type is only know after calling. If the
    288 # return type is a dictionary, the error scores can now be inserted with
    289 # the correct key.
    290 if callable(scoring):

File ~/opt/anaconda3/lib/python3.9/site-packages/sklearn/model_selection/_validation.py:367, in _warn_or_raise_about_fit_failures(results, error_score)
    360 if num_failed_fits == num_fits:
    361     all_fits_failed_message = (
    362         f"\nAll the {num_fits} fits failed.\n"
    363         "It is very likely that your model is misconfigured.\n"
    364         "You can try to debug the error by setting error_score='raise'.\n\n"
    365         f"Below are more details about the failures:\n{fit_errors_summary}"
    366     )
--> 367     raise ValueError(all_fits_failed_message)
    369 else:
    370     some_fits_failed_message = (
    371         f"\n{num_failed_fits} fits failed out of a total of {num_fits}.\n"
    372         "The score on these train-test partitions for these parameters"
   (...)
    376         f"Below are more details about the failures:\n{fit_errors_summary}"
    377     )

ValueError: 
All the 4 fits failed.
It is very likely that your model is misconfigured.
You can try to debug the error by setting error_score='raise'.

Below are more details about the failures:
--------------------------------------------------------------------------------
1 fits failed with the following error:
Traceback (most recent call last):
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/model_selection/_validation.py", line 686, in _fit_and_score
    estimator.fit(X_train, y_train, **fit_params)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/pipeline.py", line 401, in fit
    Xt = self._fit(X, y, **fit_params_steps)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/pipeline.py", line 359, in _fit
    X, fitted_transformer = fit_transform_one_cached(
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/joblib/memory.py", line 349, in __call__
    return self.func(*args, **kwargs)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/pipeline.py", line 893, in _fit_transform_one
    res = transformer.fit_transform(X, y, **fit_params)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/utils/_set_output.py", line 142, in wrapped
    data_to_wrap = f(self, X, *args, **kwargs)
  File "/var/folders/bp/jr0jjl2s67j5prj88dr9jbgh0000gq/T/ipykernel_4958/4202320140.py", line 109, in fit_transform
    return self.transform(X)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/utils/_set_output.py", line 142, in wrapped
    data_to_wrap = f(self, X, *args, **kwargs)
  File "/var/folders/bp/jr0jjl2s67j5prj88dr9jbgh0000gq/T/ipykernel_4958/4202320140.py", line 80, in transform
    new_X = pd.DataFrame(transformed_X, columns=feature_names)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/pandas/core/frame.py", line 762, in __init__
    mgr = ndarray_to_mgr(
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/pandas/core/internals/construction.py", line 349, in ndarray_to_mgr
    _check_values_indices_shape_match(values, index, columns)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/pandas/core/internals/construction.py", line 420, in _check_values_indices_shape_match
    raise ValueError(f"Shape of passed values is {passed}, indices imply {implied}")
ValueError: Shape of passed values is (112, 1), indices imply (112, 104)

--------------------------------------------------------------------------------
1 fits failed with the following error:
Traceback (most recent call last):
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/model_selection/_validation.py", line 686, in _fit_and_score
    estimator.fit(X_train, y_train, **fit_params)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/pipeline.py", line 401, in fit
    Xt = self._fit(X, y, **fit_params_steps)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/pipeline.py", line 359, in _fit
    X, fitted_transformer = fit_transform_one_cached(
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/joblib/memory.py", line 349, in __call__
    return self.func(*args, **kwargs)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/pipeline.py", line 893, in _fit_transform_one
    res = transformer.fit_transform(X, y, **fit_params)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/utils/_set_output.py", line 142, in wrapped
    data_to_wrap = f(self, X, *args, **kwargs)
  File "/var/folders/bp/jr0jjl2s67j5prj88dr9jbgh0000gq/T/ipykernel_4958/4202320140.py", line 109, in fit_transform
    return self.transform(X)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/utils/_set_output.py", line 142, in wrapped
    data_to_wrap = f(self, X, *args, **kwargs)
  File "/var/folders/bp/jr0jjl2s67j5prj88dr9jbgh0000gq/T/ipykernel_4958/4202320140.py", line 80, in transform
    new_X = pd.DataFrame(transformed_X, columns=feature_names)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/pandas/core/frame.py", line 762, in __init__
    mgr = ndarray_to_mgr(
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/pandas/core/internals/construction.py", line 349, in ndarray_to_mgr
    _check_values_indices_shape_match(values, index, columns)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/pandas/core/internals/construction.py", line 420, in _check_values_indices_shape_match
    raise ValueError(f"Shape of passed values is {passed}, indices imply {implied}")
ValueError: Shape of passed values is (112, 1), indices imply (112, 140)

--------------------------------------------------------------------------------
2 fits failed with the following error:
Traceback (most recent call last):
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/model_selection/_validation.py", line 686, in _fit_and_score
    estimator.fit(X_train, y_train, **fit_params)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/pipeline.py", line 401, in fit
    Xt = self._fit(X, y, **fit_params_steps)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/pipeline.py", line 359, in _fit
    X, fitted_transformer = fit_transform_one_cached(
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/joblib/memory.py", line 349, in __call__
    return self.func(*args, **kwargs)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/pipeline.py", line 893, in _fit_transform_one
    res = transformer.fit_transform(X, y, **fit_params)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/utils/_set_output.py", line 142, in wrapped
    data_to_wrap = f(self, X, *args, **kwargs)
  File "/var/folders/bp/jr0jjl2s67j5prj88dr9jbgh0000gq/T/ipykernel_4958/4202320140.py", line 109, in fit_transform
    return self.transform(X)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/sklearn/utils/_set_output.py", line 142, in wrapped
    data_to_wrap = f(self, X, *args, **kwargs)
  File "/var/folders/bp/jr0jjl2s67j5prj88dr9jbgh0000gq/T/ipykernel_4958/4202320140.py", line 80, in transform
    new_X = pd.DataFrame(transformed_X, columns=feature_names)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/pandas/core/frame.py", line 762, in __init__
    mgr = ndarray_to_mgr(
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/pandas/core/internals/construction.py", line 349, in ndarray_to_mgr
    _check_values_indices_shape_match(values, index, columns)
  File "/Users/mbagh/opt/anaconda3/lib/python3.9/site-packages/pandas/core/internals/construction.py", line 420, in _check_values_indices_shape_match
    raise ValueError(f"Shape of passed values is {passed}, indices imply {implied}")
ValueError: Shape of passed values is (113, 1), indices imply (113, 140)


```

### Versions

```shell
System:
    python: 3.9.12 (main, Apr  5 2022, 01:53:17)  [Clang 12.0.0 ]
executable: /Users/mbagh/opt/anaconda3/bin/python
   machine: macOS-10.16-x86_64-i386-64bit

Python dependencies:
      sklearn: 1.2.1
          pip: 21.2.4
   setuptools: 66.0.0
        numpy: 1.24.2
        scipy: 1.10.0
       Cython: 0.29.28
       pandas: 1.5.3
   matplotlib: 3.6.3
       joblib: 1.2.0
threadpoolctl: 2.2.0

Built with OpenMP: True
```

COMMENT:
That's because the `__init__` of your custom class doesn't have any explicit parameters, therefore none of the parameters are actually copied. You can test this by using clone:

```py
from sklearn.base import clone
est = CustomOneHotEncoder(handle_unknown='infrequent_if_exist', sparse_output=False, drop='first')
clone(est).drop
```

You need to explicitly define all parameters you're going to change in your custom estimator, or use a custom `__sklearn_clone__`: https://github.com/scikit-learn/scikit-learn/pull/24568

Labels: Bug, Needs Triage