Skip to content

Commit 8c35c92

Browse files
thomaspinderThomas Pinder
andauthored
Docs (#7)
* Add docs workflow * Add docs workflow * Add docs workflow * Update Index * Add build workflow --------- Co-authored-by: Thomas Pinder <[email protected]>
1 parent 4dc5271 commit 8c35c92

File tree

13 files changed

+347
-30
lines changed

13 files changed

+347
-30
lines changed

.github/workflows/docs.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Build Documentation
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- "**"
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: write
13+
14+
jobs:
15+
build-docs:
16+
concurrency: ci-${{ github.ref }}
17+
name: Build docs
18+
runs-on: "ubuntu-latest"
19+
defaults:
20+
run:
21+
shell: bash -l {0}
22+
23+
steps:
24+
# Grap the latest commit from the branch
25+
- name: Checkout the branch
26+
uses: actions/[email protected]
27+
with:
28+
persist-credentials: false
29+
30+
- name: Set up Python 3.11
31+
uses: actions/setup-python@v4
32+
with:
33+
python-version: 3.11
34+
35+
- name: Install Hatch
36+
uses: pypa/hatch@install
37+
38+
- name: Build and deploy the documentation
39+
run: hatch run docs:deploy

.github/workflows/test_docs.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Build Documentation
2+
3+
on:
4+
pull_request:
5+
workflow_dispatch:
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
build-docs:
12+
concurrency: ci-${{ github.ref }}
13+
name: Build docs
14+
runs-on: "ubuntu-latest"
15+
defaults:
16+
run:
17+
shell: bash -l {0}
18+
19+
steps:
20+
# Grap the latest commit from the branch
21+
- name: Checkout the branch
22+
uses: actions/[email protected]
23+
with:
24+
persist-credentials: false
25+
26+
- name: Set up Python 3.11
27+
uses: actions/setup-python@v4
28+
with:
29+
python-version: 3.11
30+
31+
- name: Install Hatch
32+
uses: pypa/hatch@install
33+
34+
- name: Build and deploy the documentation
35+
run: hatch run docs:build

.github/workflows/tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
uses: actions/[email protected]
2121
with:
2222
fetch-depth: 1
23+
2324
- name: Set up Python ${{ matrix.python-version }}
2425
uses: actions/setup-python@v4
2526
with:

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,5 @@ scratch_nbs/
147147
.DS_store
148148
package.json
149149
package-lock.json
150-
node_modules/
150+
node_modules/
151+
docs/_examples

docs/index.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Welcome to Causal Validation
2+
3+
Causal Validation is a library designed to validate and test your causal models. To
4+
achieve this, we provide functionality to simulate causal data, and vaildate your model
5+
through a placebo test.
6+
7+
## Data Synthesis
8+
9+
Data Synthesis in Causal Validation is a fully composable process whereby a set of
10+
functions are sequentially applied to a dataset. At some point in this process we also
11+
induce a treatment effect. Any of these functions can be parameterised to either have
12+
constant parameter values across all control units, or a value that varies across
13+
parameters. To see this, consider the below example where we simulate a dataset whose
14+
trend varies across each of the 10 control units.
15+
16+
```python
17+
from causal_validation import Config, simulate
18+
from causal_validation.effects import StaticEffect
19+
from causal_validation.plotters import plot
20+
from causal_validation.transforms import Trend, Periodic
21+
from causal_validation.transforms.parameter import UnitVaryingParameter
22+
from scipy.stats import norm
23+
24+
cfg = Config(
25+
n_control_units=10,
26+
n_pre_intervention_timepoints=60,
27+
n_post_intervention_timepoints=30,
28+
)
29+
30+
# Simulate the base observation
31+
base_data = simulate(cfg)
32+
33+
# Apply a linear trend with unit-varying intercept
34+
intercept = UnitVaryingParameter(sampling_dist = norm(0, 1))
35+
trend_component = Trend(degree=1, coefficient=0.1, intercept=intercept)
36+
trended_data = trend_component(base_data)
37+
38+
# Simulate a 5% lift in the treated unit's post-intervention data
39+
effect = StaticEffect(0.05)
40+
inflated_data = effect(trended_data)
41+
```
42+
43+
## Model Validation
44+
45+
Once a dataset has been synthesised, we may wish to validate our model using a placebo
46+
test. In Causal Validation this is straightforward and can be accomplished in
47+
combination with AZCausal by the following.
48+
49+
```python
50+
from azcausal.estimators.panel.sdid import SDID
51+
from causal_validation.validation.placebo import PlaceboTest
52+
53+
model = AZCausalWrapper(model=SDID())
54+
result = PlaceboTest(model, inflated_data).execute()
55+
result.summary()
56+
```

docs/scripts/gen_examples.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
from argparse import ArgumentParser
2+
from pathlib import Path
3+
import subprocess
4+
from concurrent.futures import ThreadPoolExecutor, as_completed
5+
import shutil
6+
from dataclasses import dataclass
7+
8+
EXCLUDE = ["utils.py"]
9+
10+
11+
def process_file(file: Path, out_file: Path | None = None, execute: bool = False):
12+
"""Converts a python file to markdown using jupytext and nbconvert."""
13+
14+
out_dir = out_file.parent
15+
command = f"cd {out_dir.as_posix()} && "
16+
17+
out_file = out_file.relative_to(out_dir).as_posix()
18+
19+
if execute:
20+
command += f"jupytext --to ipynb {file} --output - "
21+
command += (
22+
f"| jupyter nbconvert --to markdown --execute --stdin --output {out_file}"
23+
)
24+
else:
25+
command += f"jupytext --to markdown {file} --output {out_file}"
26+
27+
subprocess.run(command, shell=True, check=False)
28+
29+
30+
def is_modified(file: Path, out_file: Path):
31+
"""Check if the output file is older than the input file."""
32+
return out_file.exists() and out_file.stat().st_mtime < file.stat().st_mtime
33+
34+
35+
def main(args):
36+
# project root directory
37+
wdir = Path(__file__).parents[2]
38+
39+
# output directory
40+
out_dir: Path = args.outdir
41+
out_dir.mkdir(exist_ok=True, parents=True)
42+
43+
# copy directories in "examples" to output directory
44+
for dir in wdir.glob("examples/*"):
45+
if dir.is_dir():
46+
(out_dir / dir.name).mkdir(exist_ok=True, parents=True)
47+
for file in dir.glob("*"):
48+
# copy, not move!
49+
shutil.copy(file, out_dir / dir.name / file.name)
50+
51+
# list of files to be processed
52+
files = [f for f in wdir.glob("examples/*.py") if f.name not in EXCLUDE]
53+
print(files)
54+
55+
# process only modified files
56+
if args.only_modified:
57+
files = [f for f in files if is_modified(f, out_dir / f"{f.stem}.md")]
58+
59+
# process files in parallel
60+
if args.parallel:
61+
with ThreadPoolExecutor(max_workers=args.max_workers) as executor:
62+
futures = []
63+
for file in files:
64+
out_file = out_dir / f"{file.stem.replace('.pct', '')}.md"
65+
futures.append(
66+
executor.submit(
67+
process_file, file, out_file=out_file, execute=args.execute
68+
)
69+
)
70+
71+
for future in as_completed(futures):
72+
try:
73+
future.result()
74+
except Exception as e:
75+
print(f"Error processing file: {e}")
76+
else:
77+
for file in files:
78+
out_file = out_dir / f"{file.stem.replace('.pct', '')}.md"
79+
process_file(file, out_file=out_file, execute=args.execute)
80+
81+
82+
@dataclass
83+
class GeneratorArgs:
84+
max_workers: int = 4
85+
execute: bool = True
86+
only_modified: bool = False
87+
project_root: Path = Path(__file__).parents[2]
88+
parallel: bool = False
89+
90+
def __post_init__(self):
91+
self.outdir: Path = self.project_root / "docs" / "_examples"
92+
93+
94+
args = GeneratorArgs()
95+
main(args)

examples/azcausal.pct.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# %% [markdown]
2+
# # AZCausal Integration
3+
#
4+
# Amazon's [AZCausal](https://github.com/amazon-science/azcausal) library provides the
5+
# functionality to fit synthetic control and difference-in-difference models to your
6+
# data. Integrating the synthetic data generating process of `causal_validation` with
7+
# AZCausal is trivial, as we show in this notebook. To start, we'll simulate a toy
8+
# dataset.
9+
110
# %%
211
from azcausal.estimators.panel.sdid import SDID
312
import scipy.stats as st
@@ -14,15 +23,6 @@
1423
)
1524
from causal_validation.transforms.parameter import UnitVaryingParameter
1625

17-
# %% [markdown]
18-
# ## AZCausal Integration
19-
#
20-
# Amazon's [AZCausal](https://github.com/amazon-science/azcausal) library provides the
21-
# functionality to fit synthetic control and difference-in-difference models to your
22-
# data. Integrating the synthetic data generating process of `causal_validation` with
23-
# AZCausal is trivial, as we show in this notebook. To start, we'll simulate a toy
24-
# dataset.
25-
2626
# %%
2727
cfg = Config(
2828
n_control_units=10,
@@ -45,7 +45,7 @@
4545
plot(inflated_data)
4646

4747
# %% [markdown]
48-
# ### Fitting a model
48+
# ## Fitting a model
4949
#
5050
# We now have some very toy data on which we may apply a model. For this demonstration
5151
# we shall use the Synthetic Difference-in-Differences model implemented in AZCausal;

examples/basic.pct.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# %% [markdown]
2+
# # Data Synthesis
3+
#
4+
# In this notebook we'll demonstrate how `causal-validation` can be used to simulate
5+
# synthetic datasets. We'll start with very simple data to which a static treatment
6+
# effect may be applied. From there, we'll build up to complex datasets. Along the way,
7+
# we'll show how reproducibility can be ensured, plots can be generated, and unit-level
8+
# parameters may be specified.
9+
110
# %%
211
from itertools import product
312

examples/placebo_test.pct.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# format_version: '1.3'
1010
# jupytext_version: 1.16.4
1111
# kernelspec:
12-
# display_name: causal-validation
12+
# display_name: dev
1313
# language: python
1414
# name: python3
1515
# ---
@@ -107,7 +107,3 @@
107107
# %%
108108
did_model = AZCausalWrapper(model=DID())
109109
PlaceboTest([model, did_model], data).execute().summary()
110-
111-
# %%
112-
113-
# %%

mkdocs.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
site_name: Causal Validation
2+
repo_url: https://github.com/amazon-science/causal-validation
3+
site_url: https://amazon-science.github.io/causal-validation/
4+
repo_name: causal-validation
5+
6+
nav:
7+
- Home: index.md
8+
- Examples:
9+
- Data Synthesis: _examples/basic.md
10+
- Placebo Testing: _examples/placebo_test.md
11+
- AZCausal Integration: _examples/azcausal.md
12+
13+
theme:
14+
name: material
15+
language: en
16+
features:
17+
- navigation.instant
18+
- navigation.tracking
19+
- navigation.sections
20+
- search.suggest
21+
- content.code.copy
22+
23+
markdown_extensions:
24+
- abbr
25+
- admonition
26+
27+
plugins:
28+
- search
29+
- gen-files:
30+
scripts:
31+
- docs/scripts/gen_examples.py
32+
33+
extra:
34+
version:
35+
provider: mike
36+
37+
watch:
38+
- src

0 commit comments

Comments
 (0)