Best way to write an inplace option in a python method

Viewed 209

I'm writing a extension for sklearn preprocessing. I have problem to write a 'inplace' version of the method you would typically see in many pandas methods. So my methods can either return a new df or change the input df directly.

class OneHotEncoder(preprocessing.OneHotEncoder):
    """
    Parameters
    ----------
    categories : 'auto' or a list of array-like, default='auto'
        Categories (unique values) per feature:
        - 'auto' : Determine categories automatically from the training data.
        - list : ``categories[i]`` holds the categories expected in the ith
    
    I'm saving a lot of description here
    """
    # @_deprecate_positional_args
    def __init__(self, n_values=None, categorical_features=None,
                 categories=None, drop=None, sparse=True, dtype=np.float64,
                 handle_unknown='error'):
        super().__init__(n_values, categorical_features, categories, drop, sparse, dtype, handle_unknown)

    def encode(self, X, columns, inplace: bool = False):
        """Encode categorical features as a pandas dataframe.

        Parameters
        ----------
        X : pandas dataframe.
        columns : a list of str.
            Indicate which columns you wish to encode.
        inplace : bool, default False
            If True, do operation inplace and return None.

        Return
        ----------
        encoded_X : One-hot encoded dataframe if inplace=False
        """
        new_data = X.copy()
        if not isinstance(columns, collections.Sequence):
            raise TypeError("Input must be a sequence-like parameter 'columns'")
        if len(columns) == 0:
            raise ValueError("Input must not be empty parameter 'columns'")
        if isinstance(X, pd.DataFrame):
            encoded_n_array = super().fit_transform(new_data.loc[:, columns]).astype(self.dtype).toarray()
            # categories_dict = dict(zip(columns, self.categories_))
            col_names = []
            for i, column in enumerate(columns):
                num_categories = len(self.categories_[i])
                if self.drop == 'first':
                    # If only one category is present, the feature will be dropped entirely.
                    if num_categories != 1:
                        num_categories = len(self.categories_[i]) - 1
                        col_names.append(np.char.add(np.repeat(column + '_', num_categories), self.categories_[i][1:]))
                    else:
                        continue
                elif self.drop == 'if_binary' and num_categories == 2:
                    col_names.append(np.array([column + '' + self.categories_[0][1]]))
                else:
                    col_names.append(np.char.add(np.repeat(column + '_', num_categories), self.categories_[i]))
            col_names = np.concatenate(col_names, axis=0)
            encoded_df = pd.DataFrame(encoded_n_array, columns=col_names)
            new_data = pd.concat([new_data, encoded_df], axis=1)
            new_data = new_data.drop(columns=columns)
            if inplace:
                X = new_data         ################ here the inplace does change anything. 
            else:
                return new_data
        else:
            raise TypeError("Input must be a DataFrame parameter 'X'")

Basically I'm inputting a X to the encode method. I want to be able to change input X inplace or just return a new copy. I changed X copy new_data for many times. But unable to change X inplace. I know my way is wrong. But how to change it? Thank you.

0 Answers
Related