Repository: scikit-learn/scikit-learn
File: sklearn/ensemble/_hist_gradient_boosting/binning.py (Python)

Code before:
            if is_categorical and known_cats is None:
                raise ValueError(
                    f"Known categories for feature {f_idx} must be provided."
                )
            if not is_categorical and known_cats is not None:
                raise ValueError(
                    f"Feature {f_idx} isn't marked as a categorical feature, "
                    "but categories were passed."
                )

        self.missing_values_bin_idx_ = self.n_bins - 1

        self.bin_thresholds_ = [None] * n_features
        n_bins_non_missing = [None] * n_features

        non_cat_thresholds = Parallel(n_jobs=self.n_threads, backend="threading")(
            delayed(_find_binning_thresholds)(
                X[:, f_idx], max_bins, sample_weight=sample_weight
            )
            for f_idx in range(n_features)
            if not self.is_categorical_[f_idx]
        )
        if weighted_thresholds is not None:
            np.testing.assert_allclose(
                np.stack(weighted_thresholds), np.stack(non_cat_thresholds)
            )

        non_cat_idx = 0
        for f_idx in range(n_features):
            if self.is_categorical_[f_idx]:
                # Since categories are assumed to be encoded in
                # [0, n_cats] and since n_cats <= max_bins,
                # the thresholds *are* the unique categorical values. This will
                # lead to the correct mapping in transform()
                thresholds = known_categories[f_idx]
                n_bins_non_missing[f_idx] = thresholds.shape[0]
                self.bin_thresholds_[f_idx] = thresholds
            else:
                self.bin_thresholds_[f_idx] = non_cat_thresholds[non_cat_idx]
                n_bins_non_missing[f_idx] = self.bin_thresholds_[f_idx].shape[0] + 1
                non_cat_idx += 1

        self.n_bins_non_missing_ = np.array(n_bins_non_missing, dtype=np.uint32)
        return self

    def transform(self, X):
        """Bin data X.

        Missing values will be mapped to the last bin.

        For categorical features, the mapping will be incorrect for unknown

Review comment (bug):
I would rather not insert testing assertions into library code.

Furthermore, `weighted_thresholds` does not seem to be ever passed in any of the tests. Was this meant as temporary debug code?

Code after:
            # Sampling with replacement to implement frequency semantics
            # for sample weights. Note that we need `replace=True` even when
            # `sample_weight is None` to make sure that passing no weights is
            # statistically equivalent to passing unit weights.
            subset = rng.choice(
                X.shape[0], self.subsample, p=subsampling_probabilities, replace=True
            )
            X = X.take(subset, axis=0)

            # Add a switch to replace sample weights with None
            # since sample weights were already used in subsampling
            # and should not then be propagated to _find_binning_thresholds
            sample_weight = None

        if self.is_categorical is None:
            self.is_categorical_ = np.zeros(X.shape[1], dtype=np.uint8)
        else:
            self.is_categorical_ = np.asarray(self.is_categorical, dtype=np.uint8)

        n_features = X.shape[1]
        known_categories = self.known_categories
        if known_categories is None:
            known_categories = [None] * n_features

        # validate is_categorical and known_categories parameters
        for f_idx in range(n_features):
            is_categorical = self.is_categorical_[f_idx]
            known_cats = known_categories[f_idx]
            if is_categorical and known_cats is None:
                raise ValueError(
                    f"Known categories for feature {f_idx} must be provided."
                )
            if not is_categorical and known_cats is not None:
                raise ValueError(
                    f"Feature {f_idx} isn't marked as a categorical feature, "
                    "but categories were passed."
                )

        self.missing_values_bin_idx_ = self.n_bins - 1

        self.bin_thresholds_ = [None] * n_features
        n_bins_non_missing = [None] * n_features

        non_cat_thresholds = Parallel(n_jobs=self.n_threads, backend="threading")(
            delayed(_find_binning_thresholds)(
                X[:, f_idx], max_bins, sample_weight=sample_weight
            )
            for f_idx in range(n_features)
            if not self.is_categorical_[f_idx]
        )
        non_cat_idx = 0