Skip to content

SklearnTransformerWrapper: cross-validation error when wrapping OneHotEncoder #368

Description

@noahjgreen295

Describe the bug
I am finding that when I create an sklearn Pipeline that uses the SklearnTransformerWrapper to wrap a OneHotEncoder, I get the following error when trying to run cross-validation:

ValueError: Input contains NaN, infinity or a value too large for dtype('float64').
...
9 fits failed out of a total of 10.
The score on these train-test partitions for these parameters will be set to nan.

Below are more details about the failures:
...
ValueError: Input contains NaN, infinity or a value too large for dtype('float64').

If I do things the "old way" with an sklearn ColumnTransformer, I do not get the error.
I also don't get errors if I either: A) Score without cross-validation or B) Don't use the categorical features (i.e. remove the one-hot encoding).

To Reproduce
See code at end

Expected behavior
Cross-validation should run correctly without nan.

Desktop (please complete the following information):

  • OS: Mac OS
  • Version: Python 3.9.7, sklearn 1.0.1, feature-engine 1.2.0

Additional context
Code was copied from notebook since I could not attach it, sorry for any strange structure
Note: I am not doing train/test separation, in order to keep the question shorter.
Note: I posted on StackOverflow as well. I can remove the post if you think it's redundant - thanks!

Code

import pandas as pd
import numpy as np
from sklearn.model_selection import cross_val_score
from sklearn.metrics import mean_squared_error
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.linear_model import LinearRegression
from feature_engine.wrappers import SklearnTransformerWrapper
from feature_engine.selection import DropFeatures
from sklearn.compose import ColumnTransformer

# Sample Data
n = 20000
df = pd.DataFrame({
    "a": [["alpha", "beta", "gamma", "delta"][np.random.randint(4)] for i in range(n)],
    "b": [np.random.random() * 100 for i in range(n)],
    "c": [np.random.random() * 200 for i in range(n)],
    "d": [["east", "west"][np.random.randint(2)] for i in range(n)],
})

def make_y(x):
    add_1 = 100 if x.a in ["alpha", "beta"] else 200
    add_2 = 100 if x.d in ["east"] else 300

    return 2*x.b + 3*x.c + 2*add_1 + 5*add_2 + np.random.normal(10)

df["y"] = df.apply(make_y, axis=1)

# Test function for reproducing error
def do_test(df, pipeline, cv=True):
    X = df.drop(columns=["y"])
    y = df[["y"]]
       
    if cv:
        return cross_val_score(pipeline, X, y, scoring="neg_mean_squared_error", cv=10)
    else:
        pipeline.fit(X, y)
        y_pred = pipeline.predict(X)        
        return mean_squared_error(y, y_pred)


# SklearnTransformerWrapper-based Pipeline structure where cross-validation fails
# Will run correctly if categoricals remove, or validate on entire test set (see comments below)
pipeline_new = Pipeline(steps=[
    ("scale_b_c", SklearnTransformerWrapper(
            transformer=StandardScaler(), 
            variables=["b", "c"]
        )
    ),
    
    # Comment out this step for cross-validation to not fail
    ("encode_a_d", SklearnTransformerWrapper(
            transformer=OneHotEncoder(drop="first", sparse=False), 
            variables=["a", "d"]
        )
    ),
    
    ("cleanup", DropFeatures(["a", "d"])),
    ("model", LinearRegression())
])

# Old-style Pipeline with ColumnTransformer that works correctly:
pipeline_old = Pipeline(steps=[
    (
        "xform", ColumnTransformer([
            ("cat", OneHotEncoder(drop="first"), ["a", "d"]),
            ("num", StandardScaler(), ["b", "c"])
        ])
    ),
    ("model", LinearRegression())
])

# Run only this line to see new Pipeline issue (and comment out other do_test())
# Set cv to False to successfully score entire training set
do_test(df, pipeline_new, cv=True)

# Run only this line to see old Pipeline run correctly (and comment out other do_test())
#do_test(df, pipeline_old, cv=True)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions