0
$\begingroup$

I am relatively new to ML and in the process of learning pipelines.

I am creating a pipeline of custom transformers and get this error: AttributeError: 'numpy.ndarray' object has no attribute 'fit'. Below is the code.

I am not entirely sure where the error is occurring. Any help is appreciated

Note: I am using the King county housing data https://www.kaggle.com/harlfoxem/housesalesprediction

housing_df =pd.read_csv("kc_house_data.csv")
housing_df


class FeatureSelector(BaseEstimator,TransformerMixin):
    def __init__(self,feature_names):
    self._feature_names = feature_names
    
    def fit(self,X, y= None):
        #We will not do anything here and just return the object
        print(f'\n Fit method - Feature Selector\n')
        return self

    def transform(self,X, y= None):
        print(f'\n Transform method - Feature Selector\n')
        #we will return only the columns mentioned in the feature names
        return X[self._feature_names]

class CategoricalTansformer(BaseEstimator,TransformerMixin):
    def __init__(self, use_dates = ['year','month','day']):
        self._use_dates = use_dates
    
    def fit(self,X,y=None):
        #nothing to do here. Return the object
        print(f'\n Fit method - Categorical Transformer\n')
        return self

    # Helper functions to extract year from column 'dates'
    def get_year(self, data):
        return str(data)[:4]

    def get_month(self,data):
        return str(data)[4:6]

    def get_day(self,data):
        return str(data)[6:8]

    #Helper function thta converts values to Binary
    def create_binary(self,data):
        if data ==0:
            return 'No'
        else:
            return 'Yes'
 
    def transform(self,X,y=None):
        print(f'\n Transform method - Categorical Transformer\n')
        #Depending on the costructor argument break dates column to specified units
        for spec in self._use_dates:
            exec("X.loc[:,'{}']= X['date'].apply(self.get_{})".format(spec,spec))
        #now drop the date column
        X = X.drop('date',axis =1)
             
       #Convert the columns to binary for one hot encoding later
    
        X.loc[:,'waterfront']=X['waterfront'].apply(self.create_binary)
        X.loc[:,'view']= X['view'].apply(self.create_binary)
        X.loc[:,'yr_renovated']= X['yr_renovated'].apply(self.create_binary)
    
        # returns numpy array
        return X.values

class NumericalTransformer(BaseEstimator,TransformerMixin):

    def __init__(self,bath_per_bed =True, years_old = True):
        self._bath_per_bed = bath_per_bed
        self._years_old = years_old
    
    def fit(self, X,y=None):
        # No computations here, return object
        print(f'\n Fit method - Numerical Transformer\n')
        return self

    def transform(self,X,y=None):
        print(f'\n Transform method - Numerical Transformer\n')
        if self._bath_per_bed:
            #create a new column
            X.loc[:,'bath_per_bed'] = X['bathrooms']/X['bedrooms']
            #drop redundant column
            X.drop('bathrooms',axis =1)
        
        if self._years_old:
            #create a new column
            X.loc[:,'years_old']= 2019 - X['yr_built']
            # drop redundant column
            X.drop('yr_built',axis =1)
      

        #Converting any infinity value in the data set to NaN
        X =X.replace([np.inf,-np.inf],np.nan)
        #print(X.values)
        #returns a numpy array
        return X.values


#Categorical features to pass down the Categorical pipeline
 categorical_features =['date','waterfront','view','yr_renovated']

 #Numerical features to pass down the Numerical pipeline
 numerical_features =    ['bedrooms','bathrooms','sqft_living','sqft_lot','floors','condition',
                'grade','sqft_basement','yr_built']

 #Defining the Categorical Pipeline
 categorical_pipeline = Pipeline(steps=[
                    ('cat_selector',    FeatureSelector(categorical_features)),
                    ('cat_transformer',CategoricalTansformer()),
                    ('one_hot_encoder', OneHotEncoder(sparse=False))
                    ])
  #Defining the Numerical Pipeline
  numerical_pipeline = Pipeline(steps =[
                        ('num_selector',FeatureSelector(numerical_features)),
                        ('num_transformer', NumericalTransformer()),
                        ('imputer', SimpleImputer(strategy ='median')),
                        ('std_scaler',StandardScaler)
                    ])


  #Combining numerical and categorical pipelines using FeatureUnion
   full_pipeline = FeatureUnion(transformer_list =[
                        ('categorical_pipeline',categorical_pipeline),
                        ('numerical_pipeline',numerical_pipeline)
                    ])


#Let us add an estimator to the pipeline that was built

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split

data_X= housing_df.drop('price',axis =1)

# Note the values of y are converted to numpy values
data_y= housing_df['price'].values

X_train,X_test,y_train,y_test = train_test_split(data_X,data_y,test_size =0.2, random_state = 42)


#Now let us build the final pipeline
full_model_pipeline = Pipeline(steps =[
                        ('full_pipeline',full_pipeline),
                        ('model',LinearRegression())
                    ])

full_model_pipeline.fit(X_train,y_train)
y_pred =full_model_pipeline.predict(X_test)

Here is the full stack trace:

AttributeError                            Traceback (most recent call last)
<ipython-input-192-64f513a54376> in <module>
     16                         ])
     17 
---> 18 full_model_pipeline.fit(X_train,y_train)
     19 y_pred =full_model_pipeline.predict(X_test)

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/sklearn/pipeline.py in fit(self, X, y, **fit_params)
    339         """
    340         fit_params_steps = self._check_fit_params(**fit_params)
--> 341         Xt = self._fit(X, y, **fit_params_steps)
    342         with _print_elapsed_time('Pipeline',
    343                                  self._log_message(len(self.steps) - 1)):

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/sklearn/pipeline.py in _fit(self, X, y, **fit_params_steps)
    301                 cloned_transformer = clone(transformer)
    302             # Fit or load from cache the current transformer
--> 303             X, fitted_transformer = fit_transform_one_cached(
    304                 cloned_transformer, X, y, None,
    305                 message_clsname='Pipeline',

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/joblib/memory.py in __call__(self, *args, **kwargs)
    350 
    351     def __call__(self, *args, **kwargs):
--> 352         return self.func(*args, **kwargs)
    353 
    354     def call_and_shelve(self, *args, **kwargs):

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/sklearn/pipeline.py in _fit_transform_one(transformer, X, y, weight, message_clsname, message, **fit_params)
    752     with _print_elapsed_time(message_clsname, message):
    753         if hasattr(transformer, 'fit_transform'):
--> 754             res = transformer.fit_transform(X, y, **fit_params)
    755         else:
    756             res = transformer.fit(X, y, **fit_params).transform(X)

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/sklearn/pipeline.py in fit_transform(self, X, y, **fit_params)
    978             sum of n_components (output dimension) over transformers.
    979         """
--> 980         results = self._parallel_func(X, y, fit_params, _fit_transform_one)
    981         if not results:
    982             # All transformers are None

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/sklearn/pipeline.py in _parallel_func(self, X, y, fit_params, func)
   1000         transformers = list(self._iter())
   1001 
-> 1002         return Parallel(n_jobs=self.n_jobs)(delayed(func)(
   1003             transformer, X, y, weight,
   1004             message_clsname='FeatureUnion',

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/joblib/parallel.py in __call__(self, iterable)
   1042                 self._iterating = self._original_iterator is not None
   1043 
-> 1044             while self.dispatch_one_batch(iterator):
   1045                 pass
   1046 

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/joblib/parallel.py in dispatch_one_batch(self, iterator)
    857                 return False
    858             else:
--> 859                 self._dispatch(tasks)
    860                 return True
    861 

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/joblib/parallel.py in _dispatch(self, batch)
    775         with self._lock:
    776             job_idx = len(self._jobs)
--> 777             job = self._backend.apply_async(batch, callback=cb)
    778             # A job can complete so quickly than its callback is
    779             # called before we get here, causing self._jobs to

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/joblib/_parallel_backends.py in apply_async(self, func, callback)
    206     def apply_async(self, func, callback=None):
    207         """Schedule a func to be run"""
--> 208         result = ImmediateResult(func)
    209         if callback:
    210             callback(result)

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/joblib/_parallel_backends.py in __init__(self, batch)
    570         # Don't delay the application, to avoid keeping the input
    571         # arguments in memory
--> 572         self.results = batch()
    573 
    574     def get(self):

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/joblib/parallel.py in __call__(self)
    260         # change the default number of processes to -1
    261         with parallel_backend(self._backend, n_jobs=self._n_jobs):
--> 262             return [func(*args, **kwargs)
    263                     for func, args, kwargs in self.items]
    264 

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/joblib/parallel.py in <listcomp>(.0)
    260         # change the default number of processes to -1
    261         with parallel_backend(self._backend, n_jobs=self._n_jobs):
--> 262             return [func(*args, **kwargs)
    263                     for func, args, kwargs in self.items]
    264 

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/sklearn/utils/fixes.py in __call__(self, *args, **kwargs)
    220     def __call__(self, *args, **kwargs):
    221         with config_context(**self.config):
--> 222             return self.function(*args, **kwargs)

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/sklearn/pipeline.py in _fit_transform_one(transformer, X, y, weight, message_clsname, message, **fit_params)
    752     with _print_elapsed_time(message_clsname, message):
    753         if hasattr(transformer, 'fit_transform'):
--> 754             res = transformer.fit_transform(X, y, **fit_params)
    755         else:
    756             res = transformer.fit(X, y, **fit_params).transform(X)

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/sklearn/pipeline.py in fit_transform(self, X, y, **fit_params)
    385             fit_params_last_step = fit_params_steps[self.steps[-1][0]]
    386             if hasattr(last_step, 'fit_transform'):
--> 387                 return last_step.fit_transform(Xt, y, **fit_params_last_step)
    388             else:
    389                 return last_step.fit(Xt, y,

~/ML_Projects/Base_ML_env/env/lib/python3.9/site-packages/sklearn/base.py in fit_transform(self, X, y, **fit_params)
    697         if y is None:
    698             # fit method of arity 1 (unsupervised transformation)
--> 699             return self.fit(X, **fit_params).transform(X)
    700         else:
    701             # fit method of arity 2 (supervised transformation)

AttributeError: 'numpy.ndarray' object has no attribute 'fit'
$\endgroup$
1
$\begingroup$

It would be helpful if you could post the full stack trace, so that we can see which line your error occurs at. In general, the more information you can provide in a question, the better.

In this case, it looks like your full_model_pipeline may somehow become a numpy array. Since you have a one-element pipeline, you could try changing

full_model_pipeline = Pipeline(steps =[
                        ('full_pipeline',full_pipeline),
                        ('model',LinearRegression())
                    ])

full_model_pipeline.fit(X_train,y_train)

to

model = LinearRegression()
model.fit(X_train, y_train)
$\endgroup$
3
  • $\begingroup$ I added the full stack trace $\endgroup$ Sep 24 at 21:31
  • $\begingroup$ Did you forget to paste the stack trace? $\endgroup$
    – Josh Bone
    Sep 24 at 21:32
  • $\begingroup$ I added it to the original question $\endgroup$ Sep 24 at 21:59
0
$\begingroup$

I believe you need to add () where you add scaler to the pipeline: ('std_scaler',StandardScaler) --> ('std_scaler',StandardScaler())

$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.