Skip to content

Commit 426d837

Browse files
jeongyoonleeclaude
andcommitted
Apply black formatting to fix linting issues
- Add black as a dependency in pyproject.toml - Format test_meta_learners.py with black - Fix code style issues for CI/CD pipeline 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 2d112cd commit 426d837

File tree

5 files changed

+144
-10
lines changed

5 files changed

+144
-10
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Please analyze and fix the GitHub issue: $ARGUMENTS.
2+
3+
Follow these steps:
4+
5+
1. Use `gh issue view` to get the issue details
6+
2. Understand the problem described in the issue
7+
3. Search the codebase for relevant files
8+
4. Implement the necessary changes to fix the issue
9+
5. Write and run tests to verify the fix
10+
6. Ensure code passes linting and type checking
11+
7. Create a descriptive commit message
12+
8. Push and create a PR
13+
14+
Remember to use the GitHub CLI (`gh`) for all GitHub-related tasks.

.claude/settings.local.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(gh issue view:*)",
5+
"Bash(uv run:*)",
6+
"Bash(git checkout:*)",
7+
"Bash(git add:*)"
8+
],
9+
"deny": []
10+
}
11+
}

CLAUDE.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
CausalML is a Python package for uplift modeling and causal inference with machine learning algorithms. It provides methods to estimate Conditional Average Treatment Effect (CATE) or Individual Treatment Effect (ITE) from experimental or observational data.
8+
9+
## Development Setup
10+
11+
### Environment Setup
12+
- Python 3.9+ required (supports 3.9-3.12)
13+
- Uses `uv` as the package manager (preferred) or `pip`
14+
- Install development dependencies with `make setup_local` (sets up pre-commit hooks)
15+
16+
### Build Commands
17+
- `make build_ext`: Build Cython extensions (required before running code/tests)
18+
- `make build`: Build wheel distribution
19+
- `make install`: Install package locally
20+
- `make clean`: Clean build artifacts
21+
22+
### Testing
23+
- `make test`: Run full test suite with coverage
24+
- `pytest -vs --cov causalml/`: Direct pytest command
25+
- `pytest tests/test_specific.py`: Run specific test file
26+
- Optional test flags:
27+
- `pytest --runtf`: Include TensorFlow tests
28+
- `pytest --runtorch`: Include PyTorch tests
29+
30+
### Code Quality
31+
- Uses `black` for code formatting
32+
- Run `black .` before submitting PRs
33+
- Pre-commit hooks available via `make setup_local`
34+
- Flake8 configuration in tox.ini with max line length 120
35+
36+
## Architecture
37+
38+
### Core Module Structure
39+
```
40+
causalml/
41+
├── dataset/ # Synthetic data generation
42+
├── feature_selection/ # Feature selection utilities
43+
├── inference/ # Main inference algorithms
44+
│ ├── meta/ # Meta-learners (S, T, X, R, DR learners)
45+
│ ├── tree/ # Causal trees and uplift trees
46+
│ ├── tf/ # TensorFlow implementations (DragonNet)
47+
│ ├── torch/ # PyTorch implementations (CEVAE)
48+
│ └── iv/ # Instrumental variable methods
49+
├── metrics/ # Evaluation metrics
50+
├── optimize/ # Policy learning and optimization
51+
└── propensity.py # Propensity score modeling
52+
```
53+
54+
### Key Components
55+
56+
#### Meta-Learners (`causalml/inference/meta/`)
57+
- **BaseLearner**: Abstract base class for all meta-learners
58+
- **S-Learner**: Single model approach
59+
- **T-Learner**: Two model approach
60+
- **X-Learner**: Cross-learner with propensity scores
61+
- **R-Learner**: Robinson's R-learner
62+
- **DR-Learner**: Doubly robust learner
63+
64+
#### Tree-Based Methods (`causalml/inference/tree/`)
65+
- Causal trees and forests with Cython implementations
66+
- Uplift trees for classification problems
67+
- Custom splitting criteria for causal inference
68+
69+
#### Propensity Score Models (`causalml/propensity.py`)
70+
- **PropensityModel**: Abstract base for propensity estimation
71+
- Built-in calibration support
72+
- Clipping bounds to avoid numerical issues
73+
74+
### Cython Extensions
75+
The package includes Cython-compiled modules for performance:
76+
- Tree algorithms (`_tree`, `_criterion`, `_splitter`, `_utils`)
77+
- Causal tree components (`_builder`, causal trees)
78+
- Always run `make build_ext` after changes to .pyx files
79+
80+
## Common Workflows
81+
82+
### Adding New Meta-Learners
83+
1. Inherit from `BaseLearner` in `causalml/inference/meta/base.py`
84+
2. Implement `fit()` and `predict()` methods
85+
3. Add appropriate tests in `tests/test_meta_learners.py`
86+
87+
### Working with Tree Methods
88+
1. Cython files are in `causalml/inference/tree/`
89+
2. Rebuild extensions with `make build_ext` after changes
90+
3. Test with synthetic data from `causalml.dataset`
91+
92+
### Testing Different Backends
93+
- Core tests run without optional dependencies
94+
- TensorFlow tests: `pytest --runtf`
95+
- PyTorch tests: `pytest --runtorch`
96+
- Tests use fixtures from `tests/conftest.py` for data generation
97+
98+
### Git Operations
99+
- **Pushing branches**: Use specific SSH key for authentication:
100+
```bash
101+
GIT_SSH_COMMAND='ssh -i ~/.ssh/github_personal -o IdentitiesOnly=yes' git push -u origin branch_name
102+
```
103+
104+
## Important Notes
105+
106+
- The package uses both pandas DataFrames and numpy arrays internally
107+
- Propensity scores are clipped by default to avoid division by zero
108+
- Meta-learners support both single and multiple treatment scenarios
109+
- Tree methods include built-in visualization capabilities
110+
- Optional dependencies (TensorFlow, PyTorch) are marked clearly in tests

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dependencies = [
4040
"lightgbm",
4141
"packaging",
4242
"graphviz",
43+
"black>=25.1.0",
4344
]
4445

4546
[project.optional-dependencies]

tests/test_meta_learners.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,6 @@ def test_BaseDRLearner(generate_regression_data):
10411041
assert auuc["cate_p"] > 0.5
10421042

10431043

1044-
10451044
def test_BaseDRClassifier(generate_classification_data):
10461045
np.random.seed(RANDOM_SEED)
10471046

@@ -1050,40 +1049,39 @@ def test_BaseDRClassifier(generate_classification_data):
10501049
df["treatment_group_key"] = np.where(
10511050
df["treatment_group_key"] == CONTROL_NAME, 0, 1
10521051
)
1053-
1052+
10541053
# Extract features and outcome
10551054
y = df[CONVERSION].values
10561055
X = df[X_names].values
10571056
treatment = df["treatment_group_key"].values
10581057

10591058
learner = BaseDRClassifier(
1060-
learner=LogisticRegression(),
1061-
treatment_effect_learner=LinearRegression()
1059+
learner=LogisticRegression(), treatment_effect_learner=LinearRegression()
10621060
)
10631061

10641062
# Test fit and predict
10651063
te = learner.fit_predict(X=X, treatment=treatment, y=y)
1066-
1064+
10671065
# Check that treatment effects are returned
10681066
assert te.shape[0] == X.shape[0]
10691067
assert te.shape[1] == len(np.unique(treatment[treatment != 0]))
1070-
1068+
10711069
# Test with return_components
10721070
te, yhat_cs, yhat_ts = learner.fit_predict(
10731071
X=X, treatment=treatment, y=y, return_components=True
10741072
)
1075-
1073+
10761074
# Check that components are returned as probabilities
10771075
for group in learner.t_groups:
10781076
assert np.all((yhat_cs[group] >= 0) & (yhat_cs[group] <= 1))
10791077
assert np.all((yhat_ts[group] >= 0) & (yhat_ts[group] <= 1))
1080-
1078+
10811079
# Test separate outcome and effect learners
10821080
learner_separate = BaseDRClassifier(
10831081
control_outcome_learner=LogisticRegression(),
10841082
treatment_outcome_learner=LogisticRegression(),
1085-
treatment_effect_learner=LinearRegression()
1083+
treatment_effect_learner=LinearRegression(),
10861084
)
1087-
1085+
10881086
te_separate = learner_separate.fit_predict(X=X, treatment=treatment, y=y)
10891087
assert te_separate.shape == te.shape

0 commit comments

Comments
 (0)