//  [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