|
7 | 7 |
|
8 | 8 | import numpy as np |
9 | 9 | import pytest |
| 10 | +import sklearn |
10 | 11 | from scipy.stats import percentileofscore |
11 | 12 | from sklearn import datasets |
12 | 13 | from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor |
|
19 | 20 | assert_array_almost_equal, |
20 | 21 | assert_array_equal, |
21 | 22 | ) |
| 23 | +from sklearn.utils.fixes import parse_version |
22 | 24 | from sklearn.utils.validation import check_is_fitted |
23 | 25 |
|
24 | 26 | from quantile_forest import ExtraTreesQuantileRegressor, RandomForestQuantileRegressor |
|
30 | 32 | calc_weighted_quantile, |
31 | 33 | ) |
32 | 34 |
|
| 35 | +sklearn_version = parse_version(sklearn.__version__) |
| 36 | + |
33 | 37 | np.random.seed(0) |
34 | 38 |
|
35 | 39 | FOREST_REGRESSORS: dict[str, Any] = { |
@@ -765,8 +769,10 @@ def check_proximity_counts(name): |
765 | 769 | n_samples = len(X) |
766 | 770 | proximities = est.proximity_counts(X) |
767 | 771 | X_leaves = est.apply(X) |
| 772 | + params = (n_samples, n_samples) |
| 773 | + params = params if sklearn_version < parse_version("1.9") else params + (None,) |
768 | 774 | bootstrap_indices = np.array( |
769 | | - [_generate_sample_indices(e.random_state, n_samples, n_samples) for e in est.estimators_] |
| 775 | + [_generate_sample_indices(e.random_state, *params) for e in est.estimators_] |
770 | 776 | ) |
771 | 777 | for train_idx, train_idx_prox in enumerate(proximities): |
772 | 778 | for proximity_idx, proximity_count in train_idx_prox: |
@@ -1218,8 +1224,10 @@ def check_proximity_counts_oob(name): |
1218 | 1224 | # Check that OOB proximity counts match OOB bootstrap counts. |
1219 | 1225 | X_leaves = est.apply(X) |
1220 | 1226 | n_samples = len(X) |
| 1227 | + params = (n_samples, n_samples) |
| 1228 | + params = params if sklearn_version < parse_version("1.9") else params + (None,) |
1221 | 1229 | bootstrap_indices = np.array( |
1222 | | - [_generate_sample_indices(e.random_state, n_samples, n_samples) for e in est.estimators_] |
| 1230 | + [_generate_sample_indices(e.random_state, *params) for e in est.estimators_] |
1223 | 1231 | ) |
1224 | 1232 | for train_idx, train_idx_prox in enumerate(proximities): |
1225 | 1233 | for proximity_idx, proximity_count in train_idx_prox: |
@@ -1384,6 +1392,115 @@ def test_serialization(name, sparse_pickle, monotonic_cst, multi_target): |
1384 | 1392 | check_serialization(name, sparse_pickle, monotonic_cst, multi_target) |
1385 | 1393 |
|
1386 | 1394 |
|
| 1395 | +@pytest.mark.skipif( |
| 1396 | + sklearn_version < parse_version("1.9"), |
| 1397 | + reason="sample_weight in bootstrap functions requires scikit-learn >= 1.9", |
| 1398 | +) |
| 1399 | +@pytest.mark.parametrize("name", FOREST_REGRESSORS) |
| 1400 | +def test_sample_weight_stored_on_fit(name): |
| 1401 | + """Check that sample_weight is stored during fit.""" |
| 1402 | + X, y = datasets.make_regression(n_samples=100, n_features=4, random_state=0) |
| 1403 | + sample_weight = np.random.default_rng(0).uniform(0.1, 2.0, size=len(y)) |
| 1404 | + |
| 1405 | + ForestRegressor = FOREST_REGRESSORS[name] |
| 1406 | + |
| 1407 | + est = ForestRegressor(n_estimators=5, random_state=0) |
| 1408 | + est.fit(X, y, sample_weight=sample_weight) |
| 1409 | + assert est.sample_weight_ is not None |
| 1410 | + assert est.sample_weight_.shape[0] == len(y) |
| 1411 | + |
| 1412 | + est_no_weight = ForestRegressor(n_estimators=5, random_state=0) |
| 1413 | + est_no_weight.fit(X, y) |
| 1414 | + assert est_no_weight.sample_weight_ is None |
| 1415 | + |
| 1416 | + |
| 1417 | +@pytest.mark.skipif( |
| 1418 | + sklearn_version < parse_version("1.9"), |
| 1419 | + reason="sample_weight in bootstrap functions requires scikit-learn >= 1.9", |
| 1420 | +) |
| 1421 | +@pytest.mark.parametrize("name", FOREST_REGRESSORS) |
| 1422 | +def test_sample_weight_predict(name): |
| 1423 | + """Check that predictions work with sample_weight on sklearn 1.9+.""" |
| 1424 | + X, y = datasets.make_regression(n_samples=200, n_features=4, random_state=0) |
| 1425 | + sample_weight = np.random.default_rng(0).uniform(0.1, 2.0, size=len(y)) |
| 1426 | + |
| 1427 | + ForestRegressor = FOREST_REGRESSORS[name] |
| 1428 | + |
| 1429 | + est = ForestRegressor(n_estimators=10, bootstrap=True, random_state=0) |
| 1430 | + est.fit(X, y, sample_weight=sample_weight) |
| 1431 | + |
| 1432 | + y_pred = est.predict(X, quantiles=[0.25, 0.5, 0.75]) |
| 1433 | + assert y_pred.shape == (len(X), 3) |
| 1434 | + assert not np.isnan(y_pred).any() |
| 1435 | + |
| 1436 | + # Predictions with sample_weight should differ from uniform weights. |
| 1437 | + est_uniform = ForestRegressor(n_estimators=10, bootstrap=True, random_state=0) |
| 1438 | + est_uniform.fit(X, y) |
| 1439 | + y_pred_uniform = est_uniform.predict(X, quantiles=[0.25, 0.5, 0.75]) |
| 1440 | + assert not np.allclose(y_pred, y_pred_uniform) |
| 1441 | + |
| 1442 | + |
| 1443 | +@pytest.mark.skipif( |
| 1444 | + sklearn_version < parse_version("1.9"), |
| 1445 | + reason="sample_weight in bootstrap functions requires scikit-learn >= 1.9", |
| 1446 | +) |
| 1447 | +@pytest.mark.parametrize("name", FOREST_REGRESSORS) |
| 1448 | +def test_sample_weight_oob(name): |
| 1449 | + """Check that OOB predictions work with sample_weight on sklearn 1.9+.""" |
| 1450 | + X, y = datasets.make_regression(n_samples=500, n_features=8, noise=0.1, random_state=0) |
| 1451 | + sample_weight = np.random.default_rng(0).uniform(0.1, 2.0, size=len(y)) |
| 1452 | + |
| 1453 | + ForestRegressor = FOREST_REGRESSORS[name] |
| 1454 | + |
| 1455 | + est = ForestRegressor(n_estimators=20, bootstrap=True, oob_score=True, random_state=0) |
| 1456 | + est.fit(X, y, sample_weight=sample_weight) |
| 1457 | + |
| 1458 | + y_pred = est.predict(X, quantiles=[0.2, 0.5, 0.8], oob_score=True) |
| 1459 | + assert y_pred.shape == (len(X), 3) |
| 1460 | + |
| 1461 | + |
| 1462 | +@pytest.mark.skipif( |
| 1463 | + sklearn_version < parse_version("1.9"), |
| 1464 | + reason="sample_weight in bootstrap functions requires scikit-learn >= 1.9", |
| 1465 | +) |
| 1466 | +@pytest.mark.parametrize("name", FOREST_REGRESSORS) |
| 1467 | +def test_sample_weight_unsampled_indices(name): |
| 1468 | + """Check that _get_unsampled_indices uses sample_weight on sklearn 1.9+.""" |
| 1469 | + X, y = datasets.make_regression(n_samples=200, n_features=4, random_state=0) |
| 1470 | + sample_weight = np.random.default_rng(0).uniform(0.1, 2.0, size=len(y)) |
| 1471 | + |
| 1472 | + ForestRegressor = FOREST_REGRESSORS[name] |
| 1473 | + |
| 1474 | + est = ForestRegressor(n_estimators=5, bootstrap=True, random_state=0) |
| 1475 | + est.fit(X, y, sample_weight=sample_weight) |
| 1476 | + |
| 1477 | + with warnings.catch_warnings(): |
| 1478 | + warnings.simplefilter("ignore", UserWarning) |
| 1479 | + unsampled = est._get_unsampled_indices(est.estimators_[0]) |
| 1480 | + |
| 1481 | + assert len(unsampled) > 0 |
| 1482 | + assert all(idx < len(y) for idx in unsampled) |
| 1483 | + |
| 1484 | + |
| 1485 | +@pytest.mark.skipif( |
| 1486 | + sklearn_version < parse_version("1.9"), |
| 1487 | + reason="sample_weight in bootstrap functions requires scikit-learn >= 1.9", |
| 1488 | +) |
| 1489 | +@pytest.mark.parametrize("name", FOREST_REGRESSORS) |
| 1490 | +def test_sample_weight_proximity_counts(name): |
| 1491 | + """Check that proximity_counts works with sample_weight on sklearn 1.9+.""" |
| 1492 | + X, y = datasets.make_regression(n_samples=100, n_features=4, random_state=0) |
| 1493 | + sample_weight = np.random.default_rng(0).uniform(0.1, 2.0, size=len(y)) |
| 1494 | + |
| 1495 | + ForestRegressor = FOREST_REGRESSORS[name] |
| 1496 | + |
| 1497 | + est = ForestRegressor(n_estimators=5, bootstrap=True, random_state=0) |
| 1498 | + est.fit(X, y, sample_weight=sample_weight) |
| 1499 | + |
| 1500 | + proximities = est.proximity_counts(X) |
| 1501 | + assert len(proximities) == len(X) |
| 1502 | + |
| 1503 | + |
1387 | 1504 | def test_calc_quantile(): |
1388 | 1505 | """Check quantile calculations.""" |
1389 | 1506 | quantiles = [0.0, 0.25, 0.5, 0.75, 1.0] |
|
0 commit comments