//  [python]
def _transform(self, X, handle_unknown='error'):
        X_list, n_samples, n_features = self._check_X(X)
        
        X_int = np.zeros((n_samples, n_features), dtype=int)
        X_mask = np.ones((n_samples, n_features), dtype=bool)
        
        if n_features != len(self.categories_):
            raise ValueError(
                "The number of features in X is different to the number of "
                "features of the fitted data. The fitted data had {} features "
                "and the X has {} features."
                .format(len(self.categories_,), n_features)
            )
        
        for i in range(n_features):
            Xi = X_list[i]
            diff, valid_mask = _encode_check_unknown(Xi, self.categories_[i],
                                                     return_mask=True)
            
            if not np.all(valid_mask):
                if handle_unknown == 'error':
                    msg = ("Found unknown categories {0} in column {1}"
                           " during transform".format(diff, i))
                    raise ValueError(msg)
                else:
                    # Set the problematic rows to an acceptable value and
                    # continue `The rows are marked `X_mask` and will be
                    # removed later.
                    X_mask[:, i] = valid_mask
                    # cast Xi into the largest string type necessary
                    # to handle different lengths of numpy strings
                    if (self.categories_[i].dtype.kind in ('U', 'S')
                            and self.categories_[i].itemsize > Xi.itemsize):
                        Xi = Xi.astype(self.categories_[i].dtype)
                    else:
                        Xi = Xi.copy()
                    
                    Xi[~valid_mask] = self.categories_[i][0]
            # We use check_unknown=False, since _encode_check_unknown was
            # already called above.
            _, encoded = _encode(Xi, self.categories_[i], encode=True,
                                 check_unknown=False)
            X_int[:, i] = encoded
        
        # We need to take care of infrequent categories here. We want all the
        # infrequent categories to end up in a specific column, after all the
        # frequent ones. Let's say we have 4 categories with 2 infrequent
        # categories (and 2 frequent categories): we want the value in X_int
        # for the infrequent categories to be 2 (third and last column), and
        # the values for the frequent ones to be 0 and 1. The piece of code
        # below performs this mapping.
        # TODO: maybe integrate this part with the one above
        self._infrequent_mappings = {}
        huge_int = np.iinfo(X_int.dtype).max
        for feature_idx in range(n_features):
            if self.infrequent_indices_[feature_idx].size > 0:
                mapping = np.arange(len(self.categories_[feature_idx]))
                # Trick: set the infrequent cats columns to a very big int and
                # encode again.
                for ordinal_cat in self.infrequent_indices_[feature_idx]:
                    mapping[ordinal_cat] = huge_int
                _, mapping = _encode_numpy(mapping, encode=True)
                
                # update X_int and save mapping for later (for dropping logic)
                X_int[:, feature_idx] = mapping[X_int[:, feature_idx]]
                self._infrequent_mappings[feature_idx] = mapping
        
        return X_int, X_mask