bsts-causalimpact 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bsts_causalimpact-0.1.0/.cargo/config.toml +8 -0
- bsts_causalimpact-0.1.0/.claude/settings.json +6 -0
- bsts_causalimpact-0.1.0/.githooks/pre-commit +10 -0
- bsts_causalimpact-0.1.0/.githooks/pre-push +12 -0
- bsts_causalimpact-0.1.0/.githooks/prepare-commit-msg +44 -0
- bsts_causalimpact-0.1.0/.github/workflows/ci.yml +45 -0
- bsts_causalimpact-0.1.0/.github/workflows/numerical-equivalence.yml +83 -0
- bsts_causalimpact-0.1.0/.github/workflows/release.yml +70 -0
- bsts_causalimpact-0.1.0/.gitignore +13 -0
- bsts_causalimpact-0.1.0/.python-version +1 -0
- bsts_causalimpact-0.1.0/CODE_OF_CONDUCT.md +65 -0
- bsts_causalimpact-0.1.0/CONTRIBUTING.md +81 -0
- bsts_causalimpact-0.1.0/Cargo.lock +416 -0
- bsts_causalimpact-0.1.0/Cargo.toml +16 -0
- bsts_causalimpact-0.1.0/LICENSE +21 -0
- bsts_causalimpact-0.1.0/PKG-INFO +13 -0
- bsts_causalimpact-0.1.0/README.md +217 -0
- bsts_causalimpact-0.1.0/SECURITY.md +32 -0
- bsts_causalimpact-0.1.0/benchmarks/benchmark.py +181 -0
- bsts_causalimpact-0.1.0/benchmarks/results.md +11 -0
- bsts_causalimpact-0.1.0/docs/compatibility-matrix.md +76 -0
- bsts_causalimpact-0.1.0/docs/migration-from-r.md +94 -0
- bsts_causalimpact-0.1.0/docs/migration-from-tfp.md +102 -0
- bsts_causalimpact-0.1.0/docs/quickstart.ipynb +199 -0
- bsts_causalimpact-0.1.0/pyproject.toml +36 -0
- bsts_causalimpact-0.1.0/python/causal_impact/__init__.py +7 -0
- bsts_causalimpact-0.1.0/python/causal_impact/analysis.py +117 -0
- bsts_causalimpact-0.1.0/python/causal_impact/data.py +215 -0
- bsts_causalimpact-0.1.0/python/causal_impact/main.py +167 -0
- bsts_causalimpact-0.1.0/python/causal_impact/options.py +42 -0
- bsts_causalimpact-0.1.0/python/causal_impact/plot.py +97 -0
- bsts_causalimpact-0.1.0/python/causal_impact/summary.py +88 -0
- bsts_causalimpact-0.1.0/rust-toolchain.toml +2 -0
- bsts_causalimpact-0.1.0/scripts/generate_r_reference.R +127 -0
- bsts_causalimpact-0.1.0/src/distributions.rs +106 -0
- bsts_causalimpact-0.1.0/src/kalman.rs +173 -0
- bsts_causalimpact-0.1.0/src/lib.rs +83 -0
- bsts_causalimpact-0.1.0/src/sampler.rs +705 -0
- bsts_causalimpact-0.1.0/src/state_space.rs +46 -0
- bsts_causalimpact-0.1.0/tests/__init__.py +0 -0
- bsts_causalimpact-0.1.0/tests/conftest.py +50 -0
- bsts_causalimpact-0.1.0/tests/fixtures/r_reference_basic.json +21 -0
- bsts_causalimpact-0.1.0/tests/fixtures/r_reference_covariates.json +24 -0
- bsts_causalimpact-0.1.0/tests/fixtures/r_reference_no_effect.json +21 -0
- bsts_causalimpact-0.1.0/tests/fixtures/r_reference_strong_effect.json +21 -0
- bsts_causalimpact-0.1.0/tests/test_analysis.py +405 -0
- bsts_causalimpact-0.1.0/tests/test_data.py +181 -0
- bsts_causalimpact-0.1.0/tests/test_integration.py +148 -0
- bsts_causalimpact-0.1.0/tests/test_numerical_equivalence.py +340 -0
- bsts_causalimpact-0.1.0/tests/test_options.py +249 -0
- bsts_causalimpact-0.1.0/tests/test_plot.py +88 -0
- bsts_causalimpact-0.1.0/tests/test_rust_sampler.py +303 -0
- bsts_causalimpact-0.1.0/tests/test_spike_slab.py +478 -0
- bsts_causalimpact-0.1.0/tests/test_summary.py +77 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
echo "=== pre-push: cargo test ==="
|
|
5
|
+
cargo test
|
|
6
|
+
|
|
7
|
+
echo "=== pre-push: pytest ==="
|
|
8
|
+
if [ -f .venv/bin/pytest ]; then
|
|
9
|
+
.venv/bin/pytest tests/ --tb=short -q
|
|
10
|
+
else
|
|
11
|
+
echo "pre-push: pytest not found, skipping (CI will catch)"
|
|
12
|
+
fi
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claude Code の attribution / Co-Authored-By trailer をコミット前に削除する safety net
|
|
3
|
+
# 有効化: git config core.hooksPath .githooks
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
MSG_FILE="${1:-}"
|
|
8
|
+
if [ -z "$MSG_FILE" ] || [ ! -f "$MSG_FILE" ]; then
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
python3 - "$MSG_FILE" <<'PY'
|
|
13
|
+
import re, sys, pathlib
|
|
14
|
+
|
|
15
|
+
p = pathlib.Path(sys.argv[1])
|
|
16
|
+
text = p.read_text(encoding="utf-8", errors="replace")
|
|
17
|
+
lines = text.splitlines()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_claude_line(line: str) -> bool:
|
|
21
|
+
s = line.strip()
|
|
22
|
+
if re.match(r"^Generated with(\s+\[Claude Code\])?", s, re.IGNORECASE):
|
|
23
|
+
return True
|
|
24
|
+
if "claude.ai/code" in s or "code.claude.com" in s:
|
|
25
|
+
return True
|
|
26
|
+
if re.match(r"^Co-Authored-By:\s*Claude\b", s, re.IGNORECASE):
|
|
27
|
+
return True
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# 末尾から Claude attribution 行を削除
|
|
32
|
+
while lines and (is_claude_line(lines[-1]) or lines[-1].strip() == ""):
|
|
33
|
+
if is_claude_line(lines[-1]):
|
|
34
|
+
lines.pop()
|
|
35
|
+
while lines and lines[-1].strip() == "":
|
|
36
|
+
lines.pop()
|
|
37
|
+
else:
|
|
38
|
+
lines.pop()
|
|
39
|
+
|
|
40
|
+
out = "\n".join(lines).rstrip() + "\n"
|
|
41
|
+
p.write_text(out, encoding="utf-8")
|
|
42
|
+
PY
|
|
43
|
+
|
|
44
|
+
exit 0
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["**"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- name: Run ruff
|
|
17
|
+
run: pip install uv && uvx ruff check .
|
|
18
|
+
|
|
19
|
+
rust-test:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
24
|
+
- uses: Swatinem/rust-cache@v2
|
|
25
|
+
- name: Run Rust tests
|
|
26
|
+
run: cargo test
|
|
27
|
+
|
|
28
|
+
python-test:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
strategy:
|
|
31
|
+
matrix:
|
|
32
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
36
|
+
- uses: Swatinem/rust-cache@v2
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: ${{ matrix.python-version }}
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
run: pip install uv && uv sync --all-extras
|
|
42
|
+
- name: Run Python tests
|
|
43
|
+
run: .venv/bin/pytest tests/ -v --tb=short
|
|
44
|
+
env:
|
|
45
|
+
CI: "true"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: Numerical Equivalence
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: "0 2 * * 1"
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
# R live comparison: non-blocking (R install ~15min)
|
|
12
|
+
equivalence-with-r:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
if: github.event_name != 'schedule'
|
|
15
|
+
continue-on-error: true
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: r-lib/actions/setup-r@v2
|
|
19
|
+
with:
|
|
20
|
+
r-version: "4.4"
|
|
21
|
+
- name: Install R packages
|
|
22
|
+
run: |
|
|
23
|
+
Rscript -e "install.packages(
|
|
24
|
+
c('CausalImpact','jsonlite'),
|
|
25
|
+
repos='https://cloud.r-project.org'
|
|
26
|
+
)"
|
|
27
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
28
|
+
- uses: Swatinem/rust-cache@v2
|
|
29
|
+
- uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.12"
|
|
32
|
+
- name: Install Python dependencies
|
|
33
|
+
run: pip install uv && uv sync --all-extras
|
|
34
|
+
- name: Generate fresh R fixtures
|
|
35
|
+
run: Rscript scripts/generate_r_reference.R
|
|
36
|
+
- name: Run equivalence tests
|
|
37
|
+
run: .venv/bin/pytest tests/test_numerical_equivalence.py -v --tb=short
|
|
38
|
+
env:
|
|
39
|
+
CI: "true"
|
|
40
|
+
|
|
41
|
+
# Weekly: regenerate fixtures from R and auto-commit
|
|
42
|
+
regenerate-fixtures:
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
if: github.event_name == 'schedule'
|
|
45
|
+
permissions:
|
|
46
|
+
contents: write
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
with:
|
|
50
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
51
|
+
- uses: r-lib/actions/setup-r@v2
|
|
52
|
+
with:
|
|
53
|
+
r-version: "4.4"
|
|
54
|
+
- name: Install R packages
|
|
55
|
+
run: |
|
|
56
|
+
Rscript -e "install.packages(
|
|
57
|
+
c('CausalImpact','jsonlite'),
|
|
58
|
+
repos='https://cloud.r-project.org'
|
|
59
|
+
)"
|
|
60
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
61
|
+
- uses: Swatinem/rust-cache@v2
|
|
62
|
+
- uses: actions/setup-python@v5
|
|
63
|
+
with:
|
|
64
|
+
python-version: "3.12"
|
|
65
|
+
- name: Install Python dependencies
|
|
66
|
+
run: pip install uv && uv sync --all-extras
|
|
67
|
+
- name: Regenerate R fixtures
|
|
68
|
+
run: Rscript scripts/generate_r_reference.R
|
|
69
|
+
- name: Run equivalence tests
|
|
70
|
+
run: .venv/bin/pytest tests/test_numerical_equivalence.py -v --tb=short
|
|
71
|
+
env:
|
|
72
|
+
CI: "true"
|
|
73
|
+
- name: Commit updated fixtures if changed
|
|
74
|
+
run: |
|
|
75
|
+
git config user.name "github-actions[bot]"
|
|
76
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
77
|
+
git add tests/fixtures/r_reference_*.json
|
|
78
|
+
if git diff --staged --quiet; then
|
|
79
|
+
echo "No fixture changes, skipping commit"
|
|
80
|
+
else
|
|
81
|
+
git commit -m "chore: regenerate R fixtures (weekly update)"
|
|
82
|
+
git push
|
|
83
|
+
fi
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build-wheels:
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
include:
|
|
17
|
+
- os: ubuntu-latest
|
|
18
|
+
target: x86_64
|
|
19
|
+
- os: ubuntu-latest
|
|
20
|
+
target: aarch64
|
|
21
|
+
- os: macos-14
|
|
22
|
+
target: aarch64
|
|
23
|
+
- os: macos-latest
|
|
24
|
+
target: x86_64
|
|
25
|
+
- os: windows-latest
|
|
26
|
+
target: x64
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.12"
|
|
32
|
+
- name: Build wheels
|
|
33
|
+
uses: PyO3/maturin-action@v1
|
|
34
|
+
with:
|
|
35
|
+
target: ${{ matrix.target }}
|
|
36
|
+
args: --release --out dist -i 3.10 3.11 3.12 3.13
|
|
37
|
+
manylinux: auto
|
|
38
|
+
- uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: wheels-${{ matrix.os }}-${{ matrix.target }}
|
|
41
|
+
path: dist/*.whl
|
|
42
|
+
|
|
43
|
+
build-sdist:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- name: Build sdist
|
|
48
|
+
uses: PyO3/maturin-action@v1
|
|
49
|
+
with:
|
|
50
|
+
command: sdist
|
|
51
|
+
args: --out dist
|
|
52
|
+
- uses: actions/upload-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: sdist
|
|
55
|
+
path: dist/*.tar.gz
|
|
56
|
+
|
|
57
|
+
publish:
|
|
58
|
+
needs: [build-wheels, build-sdist]
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
environment: pypi
|
|
61
|
+
permissions:
|
|
62
|
+
id-token: write
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/download-artifact@v4
|
|
65
|
+
with:
|
|
66
|
+
path: dist
|
|
67
|
+
merge-multiple: true
|
|
68
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
69
|
+
with:
|
|
70
|
+
packages-dir: dist/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity
|
|
10
|
+
and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
|
18
|
+
community include:
|
|
19
|
+
|
|
20
|
+
- Demonstrating empathy and kindness toward other people
|
|
21
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
+
- Giving and gracefully accepting constructive feedback
|
|
23
|
+
- Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
+
and learning from the experience
|
|
25
|
+
- Focusing on what is best not just for us as individuals, but for the
|
|
26
|
+
overall community
|
|
27
|
+
|
|
28
|
+
Examples of unacceptable behavior include:
|
|
29
|
+
|
|
30
|
+
- The use of sexualized language or imagery, and sexual attention or
|
|
31
|
+
advances of any kind
|
|
32
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
+
- Public or private harassment
|
|
34
|
+
- Publishing others' private information, such as a physical or email
|
|
35
|
+
address, without their explicit permission
|
|
36
|
+
- Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
+
professional setting
|
|
38
|
+
|
|
39
|
+
## Enforcement Responsibilities
|
|
40
|
+
|
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
+
or harmful.
|
|
45
|
+
|
|
46
|
+
## Scope
|
|
47
|
+
|
|
48
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
|
49
|
+
an individual is officially representing the community in public spaces.
|
|
50
|
+
|
|
51
|
+
## Enforcement
|
|
52
|
+
|
|
53
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
54
|
+
reported to the community leaders responsible for enforcement via GitHub Issues
|
|
55
|
+
or by contacting the maintainer directly.
|
|
56
|
+
|
|
57
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
|
58
|
+
|
|
59
|
+
## Attribution
|
|
60
|
+
|
|
61
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
62
|
+
version 2.0, available at
|
|
63
|
+
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
|
64
|
+
|
|
65
|
+
[homepage]: https://www.contributor-covenant.org
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Contributing to bsts-causalimpact
|
|
2
|
+
|
|
3
|
+
Thank you for considering contributing to bsts-causalimpact.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
|
|
9
|
+
- Python 3.10+
|
|
10
|
+
- Rust toolchain (stable)
|
|
11
|
+
- uv (recommended) or pip
|
|
12
|
+
|
|
13
|
+
### Getting Started
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/YuminosukeSato/bsts-causalimpact.git
|
|
17
|
+
cd bsts-causalimpact
|
|
18
|
+
|
|
19
|
+
# Install all dependencies including Rust extension
|
|
20
|
+
uv sync --all-extras
|
|
21
|
+
|
|
22
|
+
# Set up git hooks
|
|
23
|
+
git config core.hooksPath .githooks
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Running Tests
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Full test suite
|
|
30
|
+
uv run pytest tests/ -v
|
|
31
|
+
|
|
32
|
+
# Rust unit tests
|
|
33
|
+
cargo test
|
|
34
|
+
|
|
35
|
+
# Lint check
|
|
36
|
+
uv run ruff check .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Pull Request Workflow
|
|
40
|
+
|
|
41
|
+
1. Create a feature branch from `main`
|
|
42
|
+
2. Write tests first (TDD)
|
|
43
|
+
3. Implement changes
|
|
44
|
+
4. Ensure all tests pass and ruff reports no errors
|
|
45
|
+
5. Open a PR with a clear description of changes
|
|
46
|
+
|
|
47
|
+
### Commit Messages
|
|
48
|
+
|
|
49
|
+
Use conventional commit style:
|
|
50
|
+
|
|
51
|
+
- `feat:` new features
|
|
52
|
+
- `fix:` bug fixes
|
|
53
|
+
- `test:` test additions or changes
|
|
54
|
+
- `docs:` documentation changes
|
|
55
|
+
- `refactor:` code restructuring without behavior change
|
|
56
|
+
|
|
57
|
+
### Test Requirements
|
|
58
|
+
|
|
59
|
+
- All new code must have tests
|
|
60
|
+
- Boundary values and edge cases must be covered
|
|
61
|
+
- Existing tests must not be weakened to make them pass
|
|
62
|
+
- Numerical equivalence tests (±3% tolerance with R) must stay green
|
|
63
|
+
|
|
64
|
+
## Architecture Overview
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
python/causal_impact/ # Python package
|
|
68
|
+
src/ # Rust Gibbs sampler (PyO3)
|
|
69
|
+
tests/ # pytest test suite
|
|
70
|
+
benchmarks/ # Performance benchmarks
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The Gibbs sampler runs in Rust via PyO3 bindings. Python handles data preparation, post-processing, plotting, and summary formatting.
|
|
74
|
+
|
|
75
|
+
## Reporting Issues
|
|
76
|
+
|
|
77
|
+
Open an issue on GitHub with:
|
|
78
|
+
|
|
79
|
+
- Steps to reproduce
|
|
80
|
+
- Expected vs actual behavior
|
|
81
|
+
- Python version and OS
|