Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

- **Expanded warning coverage for `Sample.from_frame()` ID inference**
- Added assertions that validate all three expected warnings are emitted when inferring an `id` column and default weights, including ID guessing, ID string casting, and automatic weight creation.
- **Added focused unit coverage for IPW helpers**
- Added tests for `link_transform()`, and `calc_dev()` to validate behavior for extreme probabilities, and finite 10-fold deviance summaries.

# 0.16.0 (2026-02-09)

Expand Down
3 changes: 0 additions & 3 deletions balance/weighting_methods/ipw.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
logger: logging.Logger = logging.getLogger(__package__)


# TODO: Add tests for model_coefs()
# TODO: Improve interpretability of model coefficients, as variables are no longer zero-centered.
def model_coefs(
model: ClassifierMixin,
Expand Down Expand Up @@ -94,7 +93,6 @@ def model_coefs(
}


# TODO: Add tests for link_transform()
def link_transform(pred: np.ndarray) -> np.ndarray:
"""Transforms probabilities into log odds (link function).

Expand Down Expand Up @@ -184,7 +182,6 @@ def _convert_to_dense_array(
return X_matrix


# TODO: Add tests for calc_dev()
def calc_dev(
X_matrix: csr_matrix,
y: np.ndarray,
Expand Down
33 changes: 33 additions & 0 deletions tests/test_ipw.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from balance.sample_class import Sample
from balance.weighting_methods import ipw as balance_ipw
from packaging.version import Version
from scipy.sparse import csr_matrix
from sklearn.ensemble import HistGradientBoostingClassifier, RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import log_loss
Expand All @@ -37,6 +38,38 @@ class TestIPW(
):
"""Test suite for Inverse Propensity Weighting (IPW) functionality."""

def test_link_transform_handles_midpoint_and_extremes(self) -> None:
"""link_transform should return finite log-odds for probabilities in [0, 1]."""

transformed = balance_ipw.link_transform(np.array([0.5, 0.0, 1.0]))
self.assertAlmostEqual(transformed[0], 0.0, places=10)
self.assertTrue(np.isfinite(transformed[1]))
self.assertTrue(np.isfinite(transformed[2]))
self.assertLess(transformed[1], 0)
self.assertGreater(transformed[2], 0)

def test_calc_dev_returns_finite_mean_and_sd(self) -> None:
"""calc_dev should run 10-fold CV and return finite deviance summary."""

rng = np.random.RandomState(42)
X = rng.normal(size=(40, 2))
y = np.array([0] * 20 + [1] * 20)
foldids = np.tile(np.arange(10), 4)
model_weights = np.ones(40)

dev_mean, dev_sd = balance_ipw.calc_dev(
csr_matrix(X),
y,
LogisticRegression(random_state=0, max_iter=300),
model_weights,
foldids,
)

self.assertTrue(np.isfinite(dev_mean))
self.assertTrue(np.isfinite(dev_sd))
self.assertGreaterEqual(dev_mean, 0.0)
self.assertGreaterEqual(dev_sd, 0.0)

def test_ipw_weights_order(self) -> None:
"""Test that IPW assigns correct relative weight ordering.

Expand Down