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

Code before:
        )
        assert bin_thresholds.shape[0] == max_bins - 1

    else:
        # We could compute approximate midpoint percentiles using the output of
        # np.unique(col_data, return_counts) instead but this is more
        # work and the performance benefit will be limited because we
        # work on a fixed-size subsample of the full data.
        # TODO: check if there is a better way to implement this
        percentiles = np.linspace(0, 100, num=max_bins + 1)
        percentiles = percentiles[1:-1]
        sample_weight = sample_weight[~missing_mask]
        sample_weight = sample_weight[sort_idx]

        bin_thresholds = np.array(
            [
                _averaged_weighted_percentile(col_data, sample_weight, percentile)
                for percentile in percentiles
            ]
        )
        assert bin_thresholds.shape[0] == max_bins - 1

        # Remove duplicated midpoints if they exist and shift
        unique_bin_values = np.unique(bin_thresholds)
        if unique_bin_values.shape[0] != bin_thresholds.shape[0]:
            bin_thresholds = unique_bin_values

    # We avoid having +inf thresholds: +inf thresholds are only allowed in
    # a "split on nan" situation.
    np.clip(bin_thresholds, a_min=None, a_max=ALMOST_INF, out=bin_thresholds)
    return bin_thresholds


class _BinMapper(TransformerMixin, BaseEstimator):
    """Transformer that maps a dataset into integer-valued bins.

    For continuous features, the bins are created in a feature-wise fashion,
    using quantiles so that each bins contains approximately the same number
    of samples. For large datasets, quantiles are computed on a subset of the
    data to speed-up the binning, but the quantiles should remain stable.

    For categorical features, the raw categorical values are expected to be
    in [0, 254] (this is not validated here though) and each category
    corresponds to a bin. All categorical values must be known at
    initialization: transform() doesn't know how to bin unknown categorical
    values. Note that transform() is only used on non-training data in the
    case of early stopping.

    Features with a small number of values may be binned into less than
    ``n_bins`` bins. The last bin (at index ``n_bins - 1``) is always reserved
    for missing values.

Review comment (suggestion):
Let's try to craft a test case to cover this line. I think the following might help trigger this case:

```python
import numpy as np
>>> col_data = np.asarray(([1] * 1000) + [2, 3, 4, 5, 6])
>>> col_data.shape
(1005,)
>>> np.unique(col_data).shape
(6,)
>>> max_bins = 4
>>> percentiles = np.linspace(0, 100, num=max_bins + 1)[1:-1]
>>> np.percentile(col_data, percentiles)
array([1., 1., 1.])
```

Then the test could also check that the result matches what we get from:

```python
>>> col_data = np.asarray(([1, 2, 3, 4, 5, 6])
>>> sample_weight = np.asarray(([1000, 1, 1, 1, 1, 1])
```

Code after:

    if len(distinct_values) == 1:
        return np.asarray([])

    if len(distinct_values) <= max_bins:
        # Calculate midpoints if distinct values <= max_bins
        bin_thresholds = sliding_window_view(distinct_values, 2).mean(axis=1)
    elif sample_weight is None:
        # We compute bin edges using the output of np.percentile with
        # the "averaged_inverted_cdf" interpolation method that is consistent
        # with the code for the sample_weight != None case.
        percentiles = np.linspace(0, 100, num=max_bins + 1)
        percentiles = percentiles[1:-1]
        bin_thresholds = np.percentile(
            col_data, percentiles, method="averaged_inverted_cdf"
        )
        assert bin_thresholds.shape[0] == max_bins - 1
    else:
        percentiles = np.linspace(0, 100, num=max_bins + 1)
        percentiles = percentiles[1:-1]
        bin_thresholds = np.array(
            [
                _weighted_percentile(col_data, sample_weight, percentile, average=True)
                for percentile in percentiles
            ]
        )
        assert bin_thresholds.shape[0] == max_bins - 1
    # Remove duplicated thresholds if they exist.
    unique_bin_values = np.unique(bin_thresholds)
    if unique_bin_values.shape[0] != bin_thresholds.shape[0]:
        bin_thresholds = unique_bin_values

    # We avoid having +inf thresholds: +inf thresholds are only allowed in
    # a "split on nan" situation.
    np.clip(bin_thresholds, a_min=None, a_max=ALMOST_INF, out=bin_thresholds)
    return bin_thresholds


class _BinMapper(TransformerMixin, BaseEstimator):
    """Transformer that maps a dataset into integer-valued bins.

    For continuous features, the bins are created in a feature-wise fashion,
    using quantiles so that each bins contains approximately the same number
    of samples. For large datasets, quantiles are computed on a subset of the
    data to speed-up the binning, but the quantiles should remain stable.

    For categorical features, the raw categorical values are expected to be
    in [0, 254] (this is not validated here though) and each category
    corresponds to a bin. All categorical values must be known at
    initialization: transform() doesn't know how to bin unknown categorical
    values. Note that transform() is only used on non-training data in the