Skip to content

[Repo Assist] test(estimator): add regression test for categorical confounders in conditional effects (#401) - #1799

Open
github-actions[bot] wants to merge 2 commits into
mainfrom
repo-assist/test-categorical-common-causes-regression-20260906-0dd1b795fb4b8d99
Open

github-actions[bot] wants to merge 2 commits into
mainfrom
repo-assist/test-categorical-common-causes-regression-20260906-0dd1b795fb4b8d99

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

🤖 This PR was created by Repo Assist, an automated AI assistant.

Summary

Adds test_categorical_common_cause_consistent_encoding to tests/causal_estimators/test_conditional_effects.py as a regression test for #401.

Background

Issue #401 reported that backdoor.linear_regression with categorical common causes (confounders) and numeric effect modifiers raised a shape mismatch:

ValueError: shapes (N, K_subset) and (K_full,) not aligned
```

The root cause: when `_estimate_conditional_effects` groups data by a numeric effect modifier, some strata may not contain all categorical levels of a confounder. The old `pd.get_dummies`-based encoding produced different column counts per stratum, mismatching the model trained on the full dataset.

The fix (already in main via the `Encoders` class in `dowhy/utils/encoding.py`) fits a `sklearn.preprocessing.OneHotEncoder` once on the full dataset and reuses it (`encoder.transform()`) for every stratum, always producing the same number of columns.

## What this PR adds

A single new test `test_categorical_common_cause_consistent_encoding`:

- Creates a 2,000-row dataset with a **3-level categorical confounder** (A/B/C) where level C is deliberately rare (4% of rows) — so it is absent from many effect-modifier strata.
- Runs `backdoor.linear_regression` with a numeric effect modifier, triggering `_estimate_conditional_effects`.
- Asserts:
  - No exception is raised (the old bug would have raised `ValueError` on subset encoding).
  - `conditional_estimates` is a non-empty `pd.Series` with all-finite values.
  - The ATE is close to the true effect (2.0 ± 0.5 tolerance).
  - The caller's DataFrame has not been mutated (no `__categorical__` temporary columns left behind).

## Test Status

```
tests/causal_estimators/test_conditional_effects.py::test_conditional_effects_single_effect_modifier PASSED
tests/causal_estimators/test_conditional_effects.py::test_conditional_effects_multiple_effect_modifiers PASSED
tests/causal_estimators/test_conditional_effects.py::test_conditional_effects_constant_multiple_effect_modifiers PASSED
tests/causal_estimators/test_conditional_effects.py::test_conditional_effects_all_missing_multiple_effect_modifiers_returns_empty_multiindex PASSED
tests/causal_estimators/test_conditional_effects.py::test_categorical_common_cause_consistent_encoding PASSED

5 passed in 3.03s (Python 3.12, pandas 3.0.5, scikit-learn 1.9)

black --check passes
isort --check passes
flake8 — no new errors introduced

Generated by 🌈 Repo Assist, see workflow run. Learn more.

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@11c9a2c442e519ff2b427bf58679f5a525353f76

…onditional effects

Add test_categorical_common_cause_consistent_encoding to
tests/causal_estimators/test_conditional_effects.py.

When _estimate_conditional_effects groups data by a numeric effect modifier,
some strata may not contain all categorical levels of a confounder. The
Encoders class must reuse the encoder fitted on the full dataset so that
feature matrix dimensions remain consistent with the fitted regression
model.

Without the Encoders fix (pre-sklearn OneHotEncoder, when pd.get_dummies was
used per-subset), the shapes would mismatch and raise:
  ValueError: shapes (N, K_subset) and (K_full,) not aligned

Regression test for #401. The test
uses a three-level categorical confounder where level C is rare (4% of rows)
so it will be absent from many effect-modifier strata, exercising the
consistent-encoding path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@emrekiciman
emrekiciman requested a balanced review from Copilot September 13, 2026 07:56
@emrekiciman
emrekiciman marked this pull request as ready for review September 13, 2026 07:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Adds a regression test to ensure conditional effect estimation remains stable when categorical confounder levels are missing from some effect-modifier strata (issue #401), and updates the module docstring to reflect the expanded regression coverage.

Changes:

  • Expanded the module docstring to document the categorical-confounder encoding regression context.
  • Added test_categorical_common_cause_consistent_encoding to validate consistent one-hot feature dimensions across strata and lack of input DataFrame mutation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +98 to +106
The categorical confounder has three levels (A, B, C), level C is rare
(4 % of rows) so it will be absent from many effect-modifier strata.
"""
rng = np.random.default_rng(0)
n = 2000
# C is deliberately rare so it will be absent from some strata
cat_cause = rng.choice(["A", "B", "C"], size=n, p=[0.48, 0.48, 0.04])
treatment = rng.integers(0, 2, size=n)
effect_modifier = rng.standard_normal(n)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made this deterministic in 33f4b6c: rows with level C are confined to the top of the effect-modifier range so they fall into a single quantile bin, and added a pre-check assertion that at least one stratum lacks level C before calling estimate_effect.

Comment on lines +129 to +130
# The caller's DataFrame must not be mutated (no __categorical__ columns)
assert list(df.columns) == ["W0", "v0", "y", "X0"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 33f4b6c to assert that no column starts with CausalEstimator.TEMP_CAT_COLUMN_PREFIX and that the original column set is unchanged, instead of the exact ordered list.

Co-authored-by: emrekiciman <5982160+emrekiciman@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants