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

Code before:
    assert_allclose(mapper_1.bin_thresholds_[0], mapper_2.bin_thresholds_[0])
    assert_array_equal(binned_1, binned_2)


@pytest.mark.parametrize("n_bins", [50, None])
def test_binmapper_weighted_vs_repeated_equivalence(global_random_seed, n_bins):
    rng = np.random.RandomState(global_random_seed)

    n_samples = 200
    X = rng.randn(n_samples, 3)
    if n_bins is None:
        n_bins = np.unique(X[:, rng.randint(3)]).shape[0] + rng.randint(5)

    sw = rng.randint(0, 5, size=n_samples)

    X_repeated = np.repeat(X, sw, axis=0)

    est_weighted = _BinMapper(n_bins=n_bins).fit(X, sample_weight=sw)

    est_repeated = _BinMapper(n_bins=n_bins).fit(X_repeated, sample_weight=None)

    bin_thresholds_weighted = est_weighted.transform(X)
    bin_thresholds_repeated = est_repeated.transform(X)

    assert_array_equal(est_weighted.bin_thresholds_, est_repeated.bin_thresholds_)
    assert_array_equal(bin_thresholds_weighted, bin_thresholds_repeated)


@pytest.mark.parametrize(
    "max_bins, scale, offset",
    [
        (3, 2, -1),
        (42, 1, 0),
        (255, 0.3, 42),
    ],
)
def test_bin_mapper_identity_small(max_bins, scale, offset):
    data = np.arange(max_bins).reshape(-1, 1) * scale + offset
    # max_bins is the number of bins for non-missing values
    n_bins = max_bins + 1
    binned = _BinMapper(n_bins=n_bins).fit_transform(data)
    assert_array_equal(binned, np.arange(max_bins).reshape(-1, 1))


@pytest.mark.parametrize(
    "max_bins_small, max_bins_large",
    [
        (2, 2),
        (3, 3),
        (4, 4),
        (42, 42),

Review comment (suggestion):
`bin_thresholds_` has values that result from arithmetic operations on floating-point values. We should therefore not use `assert_array_equal` which does exact (bitwise) comparisons, but instead tolerate some rounding errors using `assert_allclose`.

Furthermore the ordering of those lines and the variable names are confusing.

```suggestion
    assert_allclose(est_weighted.bin_thresholds_, est_repeated.bin_thresholds_)

    X_trans_weighted = est_weighted.transform(X)
    X_trans_repeated = est_repeated.transform(X)
    assert_array_equal(X_trans_weighted, X_trans_repeated)
```

EDIT: `X_trans_weighted` and `X_trans_repeated` are integer valued, so using `assert_array_equal` on them is what we want.

Code after:
    data = distinct_values[repeated_indices]
    rng.shuffle(data)
    assert_array_equal(np.unique(data), np.sort(distinct_values))

    data = data.reshape(-1, 1)

    mapper_1 = _BinMapper(n_bins=n_distinct + 1)
    binned_1 = mapper_1.fit_transform(data)
    assert_array_equal(np.unique(binned_1[:, 0]), np.arange(n_distinct))

    # Adding more bins to the mapper yields the same results (same thresholds)
    mapper_2 = _BinMapper(n_bins=min(256, n_distinct * 3) + 1)
    binned_2 = mapper_2.fit_transform(data)

    assert_allclose(mapper_1.bin_thresholds_[0], mapper_2.bin_thresholds_[0])
    assert_array_equal(binned_1, binned_2)


@pytest.mark.parametrize("n_bins", [50, 250])
def test_binmapper_weighted_vs_repeated_equivalence(global_random_seed, n_bins):
    rng = np.random.RandomState(global_random_seed)

    n_samples = 200
    X = rng.randn(n_samples, 3)
    sw = rng.randint(0, 5, size=n_samples)
    X_repeated = np.repeat(X, sw, axis=0)

    est_weighted = _BinMapper(n_bins=n_bins).fit(X, sample_weight=sw)
    est_repeated = _BinMapper(n_bins=n_bins).fit(X_repeated, sample_weight=None)
    assert_allclose(est_weighted.bin_thresholds_, est_repeated.bin_thresholds_)

    X_trans_weighted = est_weighted.transform(X)
    X_trans_repeated = est_repeated.transform(X)
    assert_array_equal(X_trans_weighted, X_trans_repeated)


# Note: we use a small number of RNG seeds to check that the tests is not seed
# dependent while keeping the statistical test valid. If we had used the
# global_random_seed fixture, it would have been expected to get some wrong
# rejections of the null hypothesis because of the large number of
# tests run by the fixture.
@pytest.mark.parametrize("seed", [0, 1, 42])
@pytest.mark.parametrize("n_bins", [3, 5])
def test_subsampled_weighted_vs_repeated_equivalence(seed, n_bins):
    rng = np.random.RandomState(seed)

    n_samples = 500
    X = rng.randn(n_samples, 3)

    sw = rng.randint(0, 5, size=n_samples)
    X_repeated = np.repeat(X, sw, axis=0)