aersn 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.
- aersn-0.1.0/CHANGELOG.md +56 -0
- aersn-0.1.0/LICENSE +21 -0
- aersn-0.1.0/MANIFEST.in +4 -0
- aersn-0.1.0/PKG-INFO +176 -0
- aersn-0.1.0/README.md +146 -0
- aersn-0.1.0/USER_GUIDE.md +275 -0
- aersn-0.1.0/VALIDATION.md +161 -0
- aersn-0.1.0/examples/mean_regions.py +50 -0
- aersn-0.1.0/examples/regression.py +28 -0
- aersn-0.1.0/pyproject.toml +49 -0
- aersn-0.1.0/setup.cfg +4 -0
- aersn-0.1.0/src/aersn/__init__.py +38 -0
- aersn-0.1.0/src/aersn/_validation.py +85 -0
- aersn-0.1.0/src/aersn/geometry.py +226 -0
- aersn-0.1.0/src/aersn/inference.py +330 -0
- aersn-0.1.0/src/aersn/path.py +71 -0
- aersn-0.1.0/src/aersn/plotting.py +97 -0
- aersn-0.1.0/src/aersn/reference.py +194 -0
- aersn-0.1.0/src/aersn/scalar.py +66 -0
- aersn-0.1.0/src/aersn.egg-info/PKG-INFO +176 -0
- aersn-0.1.0/src/aersn.egg-info/SOURCES.txt +32 -0
- aersn-0.1.0/src/aersn.egg-info/dependency_links.txt +1 -0
- aersn-0.1.0/src/aersn.egg-info/requires.txt +15 -0
- aersn-0.1.0/src/aersn.egg-info/top_level.txt +1 -0
- aersn-0.1.0/tests/fixtures/high_precision_stress.json +684 -0
- aersn-0.1.0/tests/fixtures/r_0.2.3.json +1271 -0
- aersn-0.1.0/tests/test_audit_regressions.py +165 -0
- aersn-0.1.0/tests/test_documentation.py +25 -0
- aersn-0.1.0/tests/test_failures.py +87 -0
- aersn-0.1.0/tests/test_geometry.py +100 -0
- aersn-0.1.0/tests/test_inference.py +120 -0
- aersn-0.1.0/tests/test_r_parity.py +51 -0
- aersn-0.1.0/tests/test_reference.py +111 -0
- aersn-0.1.0/tools/export_r_fixtures.R +54 -0
aersn-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changes
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- First public-release build, following the 0.1.0a3 Windows checks and TestPyPI
|
|
6
|
+
download-and-install verification.
|
|
7
|
+
- Update the version and installation instructions for PyPI, including optional
|
|
8
|
+
plotting, exact-version installation and local wheel/source installation.
|
|
9
|
+
- Retain the 0.1.0a3 statistical implementation, numerical safeguards, tests and
|
|
10
|
+
examples without changes. The package remains an early release (Beta classifier).
|
|
11
|
+
|
|
12
|
+
## 0.1.0a3
|
|
13
|
+
|
|
14
|
+
- Preserve missing-value masks in lists and tuples of array rows before numeric
|
|
15
|
+
conversion. Reject masked rows through the mean, OLS and influence interfaces;
|
|
16
|
+
retain identical results for unmasked lists, tuples and arrays.
|
|
17
|
+
- Explain how to construct a justified lower-dimensional target when the full
|
|
18
|
+
fit is rejected before an object exists. Full-model nuisance effects must be
|
|
19
|
+
retained; neither the conditioning limit nor the statistical model is changed.
|
|
20
|
+
- Qualify boundary-membership guidance for large centers and ill-conditioned
|
|
21
|
+
paths. The gauge tolerance and hypothesis-test rule are unchanged.
|
|
22
|
+
- Update installation instructions for this local candidate and distinguish them
|
|
23
|
+
from commands to use after an announced PyPI release.
|
|
24
|
+
- Print the tested artifact version and a final pass summary in the platform
|
|
25
|
+
runner; verify that the installed version matches the selected artifact.
|
|
26
|
+
|
|
27
|
+
This candidate passed 150 tests after both wheel and source installation on
|
|
28
|
+
Windows and was uploaded to TestPyPI on 27 September 2026. Both downloaded
|
|
29
|
+
artifacts matched the tested files and passed 150 tests each on macOS.
|
|
30
|
+
|
|
31
|
+
## 0.1.0a2
|
|
32
|
+
|
|
33
|
+
- Reject masked observations before converting NumPy arrays. Fully unmasked
|
|
34
|
+
masked-array containers remain accepted; no observations are silently removed.
|
|
35
|
+
- Check the path's condition number after coordinate range scaling. Values above
|
|
36
|
+
1e8 now raise `NumericalError`; exactly/numerically rank-deficient paths retain
|
|
37
|
+
`DegeneratePathError`. Original-coordinate dual constraints and objectives are
|
|
38
|
+
checked after the LP. No regularization is introduced.
|
|
39
|
+
- Add high-precision stress fixtures, including the actual Windows failure, and
|
|
40
|
+
explicit tests of rejection outside the supported numerical range. Tighten
|
|
41
|
+
ordinary R comparisons and supported affine-transformation tolerances.
|
|
42
|
+
- Default plots to independent axis scales; add `equal_scale=True`, reduce 3D
|
|
43
|
+
tick crowding, and explain how to install the optional Matplotlib dependency.
|
|
44
|
+
- Make the user guide's runnable examples work in order, distinguish the
|
|
45
|
+
model-specific schematic call, and test README and guide examples automatically.
|
|
46
|
+
- Document membership tolerance versus strict test decisions. Scalar candidate
|
|
47
|
+
lists now accept one candidate per element.
|
|
48
|
+
- Give wheel installation instructions and remove repository-relative Markdown
|
|
49
|
+
links from the package-index description. Preserve the illustrative random seed.
|
|
50
|
+
- Select artifacts by the current package version in the Windows runner. Keep
|
|
51
|
+
old-version logs separate. The unchanged external audit can be requested with
|
|
52
|
+
`--legacy-audit`; its expected-failure marks and unconditional acceptance of
|
|
53
|
+
extremely ill-conditioned paths describe 0.1.0a1, not the revised contract.
|
|
54
|
+
|
|
55
|
+
This version has not been uploaded to TestPyPI or PyPI. It does not modify the
|
|
56
|
+
separately submitted R 0.2.3 package.
|
aersn-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yongmiao Hong, Zhuo Lin, Oliver Linton, Whitney K. Newey and Jiajing Sun
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
aersn-0.1.0/MANIFEST.in
ADDED
aersn-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aersn
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Affine-equivariant adjusted-range self-normalization for time-series inference
|
|
5
|
+
Author: Yongmiao Hong, Zhuo Lin, Oliver Linton, Whitney K. Newey
|
|
6
|
+
Author-email: Jiajing Sun <jiajing.sun@gmail.com>
|
|
7
|
+
Maintainer-email: Jiajing Sun <jiajing.sun@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Project-URL: Paper, https://www.janeway.econ.cam.ac.uk/publication/affine-equivariant-adjusted-range-self-normalization
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: numpy>=1.24
|
|
18
|
+
Requires-Dist: scipy>=1.11
|
|
19
|
+
Provides-Extra: plot
|
|
20
|
+
Requires-Dist: matplotlib>=3.7; extra == "plot"
|
|
21
|
+
Provides-Extra: test
|
|
22
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
23
|
+
Requires-Dist: pytest-cov>=5; extra == "test"
|
|
24
|
+
Requires-Dist: matplotlib>=3.7; extra == "test"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
27
|
+
Requires-Dist: twine>=6; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff>=0.11; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# aersn for Python
|
|
32
|
+
|
|
33
|
+
Affine-equivariant adjusted-range self-normalization for time-series inference.
|
|
34
|
+
|
|
35
|
+
**Version 0.1.0.** This is a native
|
|
36
|
+
NumPy/SciPy implementation: installation and use require neither R nor the R
|
|
37
|
+
package. The related R package has its own version and release process.
|
|
38
|
+
|
|
39
|
+
## What it does
|
|
40
|
+
|
|
41
|
+
Given an estimate and observation-level influence contributions, `aersn` computes
|
|
42
|
+
an adjusted-range test and joint confidence region without estimating the
|
|
43
|
+
long-run covariance matrix. It also reports simultaneous intervals for linear
|
|
44
|
+
contrasts. Built-in entry points cover sample means and ordinary least squares
|
|
45
|
+
(OLS). Other asymptotically linear estimators can use `from_influence`.
|
|
46
|
+
|
|
47
|
+
For vector parameters, the self-normalizer is the convex hull of increments of
|
|
48
|
+
the centered influence path. The gauge of a vector is the smallest nonnegative
|
|
49
|
+
multiple of this hull that contains the vector. It is computed by linear programming; the
|
|
50
|
+
support in each direction is the range of the projected path. The construction
|
|
51
|
+
is affine equivariant. Two- and three-dimensional regions are polygons and
|
|
52
|
+
polyhedra, not fitted ellipses or ellipsoids.
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
Python 3.11 or newer is required. Install from PyPI, preferably in a virtual
|
|
57
|
+
environment:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
python -m pip install aersn
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
To include two- and three-dimensional plotting:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
python -m pip install "aersn[plot]"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The base installation needs NumPy and SciPy. The `plot` extra adds Matplotlib.
|
|
70
|
+
To reproduce results with this specific version:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
python -m pip install "aersn[plot]==0.1.0"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For an offline copy supplied by the authors, run
|
|
77
|
+
`python -m pip install "./aersn-0.1.0-py3-none-any.whl[plot]"` from the directory
|
|
78
|
+
containing the wheel. To install an extracted source distribution instead,
|
|
79
|
+
run `python -m pip install ".[plot]"` from its top-level directory.
|
|
80
|
+
|
|
81
|
+
## A first example
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import numpy as np
|
|
85
|
+
import aersn
|
|
86
|
+
|
|
87
|
+
rng = np.random.default_rng(10)
|
|
88
|
+
y = rng.normal(size=(300, 2))
|
|
89
|
+
for t in range(1, len(y)):
|
|
90
|
+
y[t] += 0.35 * y[t - 1]
|
|
91
|
+
|
|
92
|
+
fit = aersn.mean(y, names=["Mean 1", "Mean 2"])
|
|
93
|
+
ref = fit.reference(draws=2_000, seed=71)
|
|
94
|
+
test = fit.test([0, 0], reference=ref)
|
|
95
|
+
print(test.statistic, test.pvalue, test.mcse)
|
|
96
|
+
print(fit.confint(reference=ref))
|
|
97
|
+
|
|
98
|
+
region = fit.region(reference=ref)
|
|
99
|
+
ax = region.plot()
|
|
100
|
+
ax.figure.savefig("joint-region.png", dpi=180)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The small reference simulation above is for illustration. The default is 10,000
|
|
104
|
+
draws; increase it when Monte Carlo uncertainty affects a substantive decision.
|
|
105
|
+
Reuse a reference for the same dimension and grid, rather than simulating it
|
|
106
|
+
again for each null hypothesis. Reusing a reference from a different sample
|
|
107
|
+
size, dimension or grid raises an error.
|
|
108
|
+
The particular random sample may reject its generating mean; the example is
|
|
109
|
+
not a demonstration of an exact finite-sample rejection rate.
|
|
110
|
+
|
|
111
|
+
## Statistical interpretation
|
|
112
|
+
|
|
113
|
+
- The input convention is
|
|
114
|
+
`sqrt(n) * (estimate - theta) = sum(psi_true) / sqrt(n) + o_p(1)`.
|
|
115
|
+
Rows of `psi` are observations in time order, not resampled or sorted records.
|
|
116
|
+
- A functional central limit theorem, asymptotic linearity and a valid estimated
|
|
117
|
+
influence path are required. Numerical full rank does not verify these assumptions.
|
|
118
|
+
- Matched-grid Brownian quantiles account for the grid used to evaluate the
|
|
119
|
+
reference path. They do not make inference finite-sample exact for general
|
|
120
|
+
dependent data.
|
|
121
|
+
- `confint` and `contrast` project a joint region. These are simultaneous
|
|
122
|
+
intervals, not separately constructed marginal intervals.
|
|
123
|
+
- A supplied nonlinear variance-accumulation profile needs a model-specific
|
|
124
|
+
justification. Merely substituting such a profile for a sample mean does not
|
|
125
|
+
construct the required path. See `USER_GUIDE.md` in the source distribution.
|
|
126
|
+
- Missing values, including masked NumPy entries, are rejected. Paths that
|
|
127
|
+
remain extremely ill-conditioned after coordinate scaling raise an error;
|
|
128
|
+
the package does not regularize them silently.
|
|
129
|
+
|
|
130
|
+
## Documentation and examples
|
|
131
|
+
|
|
132
|
+
The source distribution includes:
|
|
133
|
+
|
|
134
|
+
- `USER_GUIDE.md`: statistical interpretation and API conventions.
|
|
135
|
+
- `VALIDATION.md`: numerical validation and release checks.
|
|
136
|
+
- `examples/mean_regions.py`: executable 2D and 3D example.
|
|
137
|
+
- `examples/regression.py`: OLS and influence-contribution example.
|
|
138
|
+
|
|
139
|
+
Run the examples from this directory:
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
python examples/mean_regions.py
|
|
143
|
+
python examples/regression.py
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Figures are saved in `examples/output/` with labeled axes. Function and class
|
|
147
|
+
docstrings are also available through Python's `help()`.
|
|
148
|
+
Plots use independent axis scales by default, which is useful for parameters
|
|
149
|
+
measured in different units. Use `region.plot(equal_scale=True)` for equal
|
|
150
|
+
distance per data unit. Both choices display the same computed vertices.
|
|
151
|
+
|
|
152
|
+
## Development
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
python -m pip install -e ".[plot,test,dev]"
|
|
156
|
+
python -m pytest --cov=aersn
|
|
157
|
+
python -m ruff check .
|
|
158
|
+
python -m build
|
|
159
|
+
python -m twine check dist/*
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Tests use fixed inputs and results exported from R `aersn` 0.2.3. They do not
|
|
163
|
+
require R. The LP is also checked against an independent primal calculation.
|
|
164
|
+
This release implements the adjusted-range method, not the five comparison
|
|
165
|
+
methods or every model-specific interface in the R package.
|
|
166
|
+
|
|
167
|
+
## Paper and authors
|
|
168
|
+
|
|
169
|
+
Yongmiao Hong, Zhuo Lin, Oliver Linton, Whitney K. Newey and Jiajing Sun (2026),
|
|
170
|
+
*Affine-Equivariant Adjusted-Range Self-Normalization*, Cambridge Working Papers
|
|
171
|
+
in Economics, No. 2678. The paper is available as a
|
|
172
|
+
[Cambridge working paper](https://www.janeway.econ.cam.ac.uk/publication/affine-equivariant-adjusted-range-self-normalization).
|
|
173
|
+
This citation does not imply journal acceptance.
|
|
174
|
+
|
|
175
|
+
Copyright belongs to the five authors. Distributed under the MIT license.
|
|
176
|
+
Maintainer: Jiajing Sun, <jiajing.sun@gmail.com>.
|
aersn-0.1.0/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# aersn for Python
|
|
2
|
+
|
|
3
|
+
Affine-equivariant adjusted-range self-normalization for time-series inference.
|
|
4
|
+
|
|
5
|
+
**Version 0.1.0.** This is a native
|
|
6
|
+
NumPy/SciPy implementation: installation and use require neither R nor the R
|
|
7
|
+
package. The related R package has its own version and release process.
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
Given an estimate and observation-level influence contributions, `aersn` computes
|
|
12
|
+
an adjusted-range test and joint confidence region without estimating the
|
|
13
|
+
long-run covariance matrix. It also reports simultaneous intervals for linear
|
|
14
|
+
contrasts. Built-in entry points cover sample means and ordinary least squares
|
|
15
|
+
(OLS). Other asymptotically linear estimators can use `from_influence`.
|
|
16
|
+
|
|
17
|
+
For vector parameters, the self-normalizer is the convex hull of increments of
|
|
18
|
+
the centered influence path. The gauge of a vector is the smallest nonnegative
|
|
19
|
+
multiple of this hull that contains the vector. It is computed by linear programming; the
|
|
20
|
+
support in each direction is the range of the projected path. The construction
|
|
21
|
+
is affine equivariant. Two- and three-dimensional regions are polygons and
|
|
22
|
+
polyhedra, not fitted ellipses or ellipsoids.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
Python 3.11 or newer is required. Install from PyPI, preferably in a virtual
|
|
27
|
+
environment:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
python -m pip install aersn
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
To include two- and three-dimensional plotting:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
python -m pip install "aersn[plot]"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The base installation needs NumPy and SciPy. The `plot` extra adds Matplotlib.
|
|
40
|
+
To reproduce results with this specific version:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
python -m pip install "aersn[plot]==0.1.0"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For an offline copy supplied by the authors, run
|
|
47
|
+
`python -m pip install "./aersn-0.1.0-py3-none-any.whl[plot]"` from the directory
|
|
48
|
+
containing the wheel. To install an extracted source distribution instead,
|
|
49
|
+
run `python -m pip install ".[plot]"` from its top-level directory.
|
|
50
|
+
|
|
51
|
+
## A first example
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import numpy as np
|
|
55
|
+
import aersn
|
|
56
|
+
|
|
57
|
+
rng = np.random.default_rng(10)
|
|
58
|
+
y = rng.normal(size=(300, 2))
|
|
59
|
+
for t in range(1, len(y)):
|
|
60
|
+
y[t] += 0.35 * y[t - 1]
|
|
61
|
+
|
|
62
|
+
fit = aersn.mean(y, names=["Mean 1", "Mean 2"])
|
|
63
|
+
ref = fit.reference(draws=2_000, seed=71)
|
|
64
|
+
test = fit.test([0, 0], reference=ref)
|
|
65
|
+
print(test.statistic, test.pvalue, test.mcse)
|
|
66
|
+
print(fit.confint(reference=ref))
|
|
67
|
+
|
|
68
|
+
region = fit.region(reference=ref)
|
|
69
|
+
ax = region.plot()
|
|
70
|
+
ax.figure.savefig("joint-region.png", dpi=180)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The small reference simulation above is for illustration. The default is 10,000
|
|
74
|
+
draws; increase it when Monte Carlo uncertainty affects a substantive decision.
|
|
75
|
+
Reuse a reference for the same dimension and grid, rather than simulating it
|
|
76
|
+
again for each null hypothesis. Reusing a reference from a different sample
|
|
77
|
+
size, dimension or grid raises an error.
|
|
78
|
+
The particular random sample may reject its generating mean; the example is
|
|
79
|
+
not a demonstration of an exact finite-sample rejection rate.
|
|
80
|
+
|
|
81
|
+
## Statistical interpretation
|
|
82
|
+
|
|
83
|
+
- The input convention is
|
|
84
|
+
`sqrt(n) * (estimate - theta) = sum(psi_true) / sqrt(n) + o_p(1)`.
|
|
85
|
+
Rows of `psi` are observations in time order, not resampled or sorted records.
|
|
86
|
+
- A functional central limit theorem, asymptotic linearity and a valid estimated
|
|
87
|
+
influence path are required. Numerical full rank does not verify these assumptions.
|
|
88
|
+
- Matched-grid Brownian quantiles account for the grid used to evaluate the
|
|
89
|
+
reference path. They do not make inference finite-sample exact for general
|
|
90
|
+
dependent data.
|
|
91
|
+
- `confint` and `contrast` project a joint region. These are simultaneous
|
|
92
|
+
intervals, not separately constructed marginal intervals.
|
|
93
|
+
- A supplied nonlinear variance-accumulation profile needs a model-specific
|
|
94
|
+
justification. Merely substituting such a profile for a sample mean does not
|
|
95
|
+
construct the required path. See `USER_GUIDE.md` in the source distribution.
|
|
96
|
+
- Missing values, including masked NumPy entries, are rejected. Paths that
|
|
97
|
+
remain extremely ill-conditioned after coordinate scaling raise an error;
|
|
98
|
+
the package does not regularize them silently.
|
|
99
|
+
|
|
100
|
+
## Documentation and examples
|
|
101
|
+
|
|
102
|
+
The source distribution includes:
|
|
103
|
+
|
|
104
|
+
- `USER_GUIDE.md`: statistical interpretation and API conventions.
|
|
105
|
+
- `VALIDATION.md`: numerical validation and release checks.
|
|
106
|
+
- `examples/mean_regions.py`: executable 2D and 3D example.
|
|
107
|
+
- `examples/regression.py`: OLS and influence-contribution example.
|
|
108
|
+
|
|
109
|
+
Run the examples from this directory:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
python examples/mean_regions.py
|
|
113
|
+
python examples/regression.py
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Figures are saved in `examples/output/` with labeled axes. Function and class
|
|
117
|
+
docstrings are also available through Python's `help()`.
|
|
118
|
+
Plots use independent axis scales by default, which is useful for parameters
|
|
119
|
+
measured in different units. Use `region.plot(equal_scale=True)` for equal
|
|
120
|
+
distance per data unit. Both choices display the same computed vertices.
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
python -m pip install -e ".[plot,test,dev]"
|
|
126
|
+
python -m pytest --cov=aersn
|
|
127
|
+
python -m ruff check .
|
|
128
|
+
python -m build
|
|
129
|
+
python -m twine check dist/*
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Tests use fixed inputs and results exported from R `aersn` 0.2.3. They do not
|
|
133
|
+
require R. The LP is also checked against an independent primal calculation.
|
|
134
|
+
This release implements the adjusted-range method, not the five comparison
|
|
135
|
+
methods or every model-specific interface in the R package.
|
|
136
|
+
|
|
137
|
+
## Paper and authors
|
|
138
|
+
|
|
139
|
+
Yongmiao Hong, Zhuo Lin, Oliver Linton, Whitney K. Newey and Jiajing Sun (2026),
|
|
140
|
+
*Affine-Equivariant Adjusted-Range Self-Normalization*, Cambridge Working Papers
|
|
141
|
+
in Economics, No. 2678. The paper is available as a
|
|
142
|
+
[Cambridge working paper](https://www.janeway.econ.cam.ac.uk/publication/affine-equivariant-adjusted-range-self-normalization).
|
|
143
|
+
This citation does not imply journal acceptance.
|
|
144
|
+
|
|
145
|
+
Copyright belongs to the five authors. Distributed under the MIT license.
|
|
146
|
+
Maintainer: Jiajing Sun, <jiajing.sun@gmail.com>.
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Using aersn in Python
|
|
2
|
+
|
|
3
|
+
## 1. Observations, estimates and influence contributions
|
|
4
|
+
|
|
5
|
+
Let `estimate` contain q estimated parameters. The input `psi` is an n-by-q
|
|
6
|
+
matrix: row t is the estimated influence contribution of observation t, and
|
|
7
|
+
column j corresponds to parameter j. Observations must remain in time order.
|
|
8
|
+
The sample size is always the number of rows; it is not the number of grid
|
|
9
|
+
nodes, regression coefficients or reference draws.
|
|
10
|
+
|
|
11
|
+
The centered path is
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
G[k] = (sum(psi[:k]) - nodes[k] * sum(psi)) / sqrt(n), k=0,...,n.
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
With the default `nodes[k]=k/n`, this is calendar-time centering. Both endpoints
|
|
18
|
+
are zero. The increment hull is `K = conv{G[i]-G[j]}`. For a null vector v,
|
|
19
|
+
the statistic is `gamma_K(sqrt(n)*(estimate-v))`. Larger values provide greater
|
|
20
|
+
evidence against the null.
|
|
21
|
+
The gauge `gamma_K(z)` is the smallest nonnegative number a such that z belongs
|
|
22
|
+
to a*K.
|
|
23
|
+
|
|
24
|
+
For the sample mean, use `aersn.mean(y)`; it calculates the mean and contributions
|
|
25
|
+
`y-y.mean(axis=0)`. The following equivalent influence-contribution construction
|
|
26
|
+
also provides the runnable example used in Sections 2 and 3:
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import numpy as np
|
|
30
|
+
import aersn
|
|
31
|
+
|
|
32
|
+
y = np.random.default_rng(10).normal(size=(300, 2))
|
|
33
|
+
estimate = y.mean(axis=0)
|
|
34
|
+
psi = y - estimate
|
|
35
|
+
fit = aersn.from_influence(estimate, psi, names=["parameter 1", "parameter 2"])
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The contributions must account for all estimated nuisance parameters. For
|
|
39
|
+
example, the influence contribution of an ordinary least squares (OLS) slope
|
|
40
|
+
depends on the inverse moment matrix of the full regression, not just that
|
|
41
|
+
slope's regressor. Missing values (including NumPy masked entries) and nonfinite
|
|
42
|
+
values are rejected rather than silently
|
|
43
|
+
dropped; deleting time observations can change the statistical problem.
|
|
44
|
+
|
|
45
|
+
## 2. Tests and reference distributions
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
ref = fit.reference(draws=10_000, seed=1)
|
|
49
|
+
result = fit.test([0, 0], reference=ref, level=0.95)
|
|
50
|
+
print(result.statistic, result.critical_value, result.reject)
|
|
51
|
+
print(result.pvalue, result.mcse, result.tail_interval)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Here `level=0.95` means a 5 percent test and a 95 percent confidence region.
|
|
55
|
+
The default alternative is `"two-sided"`. Rejection occurs when the statistic
|
|
56
|
+
strictly exceeds the estimated reference quantile. For a scalar parameter,
|
|
57
|
+
`alternative="greater"` or `"less"` uses the signed endpoint-to-range ratio.
|
|
58
|
+
One-sided alternatives are not available for a joint vector hypothesis.
|
|
59
|
+
|
|
60
|
+
For each reference draw, Brownian increments have variances equal to the grid
|
|
61
|
+
spacings. They form a Brownian bridge at those nodes. An independent standard
|
|
62
|
+
normal vector provides the numerator. The same gauge calculation is used for
|
|
63
|
+
data and reference paths. A failed draw stops the calculation instead of being
|
|
64
|
+
discarded. The generator is a local NumPy PCG64 generator; the global random
|
|
65
|
+
state is unchanged. With the same environment, seed, dimension and grid, the
|
|
66
|
+
first B draws remain the same when more draws are requested.
|
|
67
|
+
|
|
68
|
+
Empirical quantiles use the median-unbiased convention, equivalent to R's
|
|
69
|
+
`quantile(..., type=8)`. Reference p-values are the fraction of draws at least
|
|
70
|
+
as large as the observed statistic, matching R `aersn` 0.2.3. They do not use
|
|
71
|
+
a plus-one correction. `mcse` is the binomial Monte Carlo standard error, and
|
|
72
|
+
`tail_interval` is a 95 percent binomial interval for the reference tail
|
|
73
|
+
probability. A reported zero p-value does not establish that the true tail
|
|
74
|
+
probability is zero. These quantities describe simulation error, not the
|
|
75
|
+
accuracy of the statistical approximation for the observed data.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
print(ref.quantile([0.90, 0.95, 0.99]))
|
|
79
|
+
print(ref.quantile_mcse([0.90, 0.95, 0.99]))
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Quantile standard errors are estimated using interleaved batches. Tail counts
|
|
83
|
+
and interpolated quantiles need not give exactly identical decisions at a
|
|
84
|
+
finite Monte Carlo boundary. The reported `reject` always uses the critical
|
|
85
|
+
value comparison.
|
|
86
|
+
|
|
87
|
+
For a scalar parameter only, an analytic alternative is available:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
ref_continuous = aersn.Reference.continuous()
|
|
91
|
+
print(aersn.scalar_quantile([0.90, 0.95, 0.99]))
|
|
92
|
+
# approximately 1.397390, 1.705776, 2.367378
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This is the continuous-path reference, not the matched finite-grid reference.
|
|
96
|
+
`scalar_cdf` and `scalar_quantile` describe the absolute ratio; `scalar_density`
|
|
97
|
+
is the density of the signed ratio. Its density integrates to one over the
|
|
98
|
+
whole real line.
|
|
99
|
+
|
|
100
|
+
## 3. Confidence regions and linear contrasts
|
|
101
|
+
|
|
102
|
+
At a reference quantile c, the joint region is
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
C = estimate + c/sqrt(n) * K.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
region = fit.region(reference=ref, level=0.95)
|
|
110
|
+
print(region.contains([0, 0]))
|
|
111
|
+
print(region.confint())
|
|
112
|
+
intervals = region.contrast([[1, -1], [0.5, 0.5]])
|
|
113
|
+
print(intervals.estimate, intervals.lower, intervals.upper)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Each contrast interval is `a @ estimate +/- c*range(G @ a)/sqrt(n)`.
|
|
117
|
+
These intervals use the q-dimensional critical value and inherit simultaneous
|
|
118
|
+
coverage from the joint region. They are not tests of a single contrast using
|
|
119
|
+
a one-dimensional critical value. For that different question, first form a
|
|
120
|
+
lower-dimensional target and then generate the corresponding reference:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
target = fit.transform([[1, -1]], names=["difference"])
|
|
124
|
+
target_ref = target.reference(draws=10_000, seed=1)
|
|
125
|
+
target.test(0, reference=target_ref)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`region.contains` permits a default tolerance of 1e-9 in region-gauge units;
|
|
129
|
+
`fit.test` uses the strict critical-value rule.
|
|
130
|
+
Their Boolean answers can differ within numerical rounding distance of a
|
|
131
|
+
boundary. `region.contains(..., tol=0)` removes the membership tolerance but
|
|
132
|
+
does not remove floating-point roundoff. Computed vertices can fall outside
|
|
133
|
+
this tolerance when the estimate is large relative to the region width or
|
|
134
|
+
the path is ill-conditioned; membership at such boundary points is sensitive
|
|
135
|
+
to rounding.
|
|
136
|
+
|
|
137
|
+
For a nonsingular q-by-q transformation H, `fit.transform(H, offset=b)` implements
|
|
138
|
+
`H @ estimate + b` and `psi @ H.T`. The corresponding confidence region is the
|
|
139
|
+
same affine transformation of the original region, up to numerical precision.
|
|
140
|
+
The original reference can then be reused because q and the grid are unchanged.
|
|
141
|
+
|
|
142
|
+
## 4. Regression
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
import numpy as np
|
|
146
|
+
import aersn
|
|
147
|
+
|
|
148
|
+
rng = np.random.default_rng(19)
|
|
149
|
+
x = rng.normal(size=(200, 2))
|
|
150
|
+
y_ols = 1 + x @ [0.5, -0.3] + rng.normal(size=200)
|
|
151
|
+
fit_ols = aersn.ols(y_ols, x) # an intercept is added
|
|
152
|
+
print(fit_ols.names, fit_ols.estimate)
|
|
153
|
+
# For two regressors, select the first slope, retaining nuisance effects:
|
|
154
|
+
slope = fit_ols.transform([[0, 1, 0]], names=["first slope"])
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
To use a design matrix that already contains an intercept, set `intercept=False`.
|
|
158
|
+
The interface uses QR decomposition and calendar-time centering. It assumes
|
|
159
|
+
the usual asymptotic linear representation and strong identification. Generalized
|
|
160
|
+
method of moments (GMM) and conditional-likelihood convenience interfaces are
|
|
161
|
+
not yet included. Their appropriately constructed influence contributions can
|
|
162
|
+
be supplied through `from_influence`; the package does not fit those models.
|
|
163
|
+
|
|
164
|
+
## 5. Two- and three-dimensional figures
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
import numpy as np
|
|
168
|
+
import aersn
|
|
169
|
+
|
|
170
|
+
data3 = np.random.default_rng(21).normal(size=(200, 3))
|
|
171
|
+
fit3 = aersn.mean(data3, names=["Mean 1", "Mean 2", "Mean 3"])
|
|
172
|
+
ref3 = fit3.reference(draws=2_000, seed=7)
|
|
173
|
+
region3 = fit3.region(reference=ref3)
|
|
174
|
+
ax = region3.plot()
|
|
175
|
+
ax.figure.savefig("region.png", dpi=180)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
For a coordinate projection, specify two or three coordinate indices. For
|
|
179
|
+
example, project this three-dimensional region onto its first and third axes:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
ax = region3.plot(coords=[0, 2]) # Python uses zero-based indices
|
|
183
|
+
vertices = region3.projection([0, 1, 2])
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
This is a projection of the full joint region, not a slice holding other
|
|
187
|
+
parameters fixed. Axis labels use the parameter names. Boundaries come from
|
|
188
|
+
actual hull vertices, with straight edges and planar faces wherever present.
|
|
189
|
+
No smoothing is applied. There is no general requirement that the region
|
|
190
|
+
become an ellipse or ellipsoid as the sample grows.
|
|
191
|
+
Axes have independent scales by default. `region3.plot(equal_scale=True)` gives
|
|
192
|
+
equal distance per data unit instead. Changing this option changes the display,
|
|
193
|
+
not the numerical region, and can make coefficients with different units harder
|
|
194
|
+
to compare visually.
|
|
195
|
+
|
|
196
|
+
The gauge uses only the path values. Explicit plotting computes differences
|
|
197
|
+
of path-hull vertices; it can be more expensive. A `max_pairs` guard on
|
|
198
|
+
`region.vertices()` prevents unexpectedly large allocations. It does not
|
|
199
|
+
restrict the gauge calculation.
|
|
200
|
+
|
|
201
|
+
## 6. Nonuniform variance accumulation
|
|
202
|
+
|
|
203
|
+
`from_influence` accepts a function of k/n or a vector of n+1 nodes. This is a
|
|
204
|
+
schematic call: `estimate` and `justified_psi` must come from an appropriate
|
|
205
|
+
model-specific construction, not the sample-mean example above.
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
fit = aersn.from_influence(estimate, justified_psi, profile=lambda r: r**3)
|
|
209
|
+
ref = fit.reference(draws=10_000, seed=1)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
This interface is appropriate only when the model satisfies a common scalar
|
|
213
|
+
variance-accumulation condition and the estimated path consistently approximates
|
|
214
|
+
the required profile-centered path. The code checks nondecreasing nodes and
|
|
215
|
+
endpoints, not these statistical conditions. In particular, demeaned sample-mean
|
|
216
|
+
contributions sum to zero. Changing the nodes then leaves the fitted path
|
|
217
|
+
unchanged and generally does not construct the required profile-centered path.
|
|
218
|
+
For this reason `mean` and `ols` use calendar centering rather than offering
|
|
219
|
+
a profile argument. Consult the paper's conditional-score construction for an
|
|
220
|
+
example where feasible profile inference is justified.
|
|
221
|
+
|
|
222
|
+
## 7. Rank, numerical accuracy and reproducibility
|
|
223
|
+
|
|
224
|
+
`fit.hull.rank`, `condition_number` and `coordinate_ranges` describe the path.
|
|
225
|
+
The condition number is the ratio of largest to smallest singular values of
|
|
226
|
+
the original centered path matrix, not of a covariance estimator. Numerical
|
|
227
|
+
rank is assessed after coordinate range scaling. The LP also uses pivoted QR
|
|
228
|
+
to handle unequal units and near collinearity. No ridge or random jitter is
|
|
229
|
+
added to singular paths. Such paths raise `DegeneratePathError`.
|
|
230
|
+
|
|
231
|
+
`scaled_condition_number` reports the condition number after dividing each
|
|
232
|
+
coordinate by its range. Values above 1e8 raise `NumericalError`, even if the
|
|
233
|
+
path has numerical full rank. This conservative double-precision limit does
|
|
234
|
+
not claim an exact error bound. Unit differences alone do not trigger it.
|
|
235
|
+
The recovered LP direction is also checked in the original coordinates.
|
|
236
|
+
When a path is rejected, inspect near-dependent parameters and consider a
|
|
237
|
+
lower-dimensional target only when it is justified by the scientific question.
|
|
238
|
+
No unreliable statistic, confidence region, or reference draw is returned.
|
|
239
|
+
|
|
240
|
+
If `mean`, `ols` or `from_influence` raises during construction, no `fit` object
|
|
241
|
+
exists, so `fit.transform(A)` is not available. With a reliably computed
|
|
242
|
+
full-model estimate and influence contributions obtained separately, construct
|
|
243
|
+
the target directly as `aersn.from_influence(A @ estimate, psi @ A.T)`.
|
|
244
|
+
For OLS, the contributions must come from the full regression and retain
|
|
245
|
+
nuisance-parameter effects; simply dropping regressors changes the model.
|
|
246
|
+
The target path must itself pass the numerical checks, and its statistical
|
|
247
|
+
validity still requires the assumptions stated above.
|
|
248
|
+
|
|
249
|
+
R and Python use different random generators and LP solvers. Equal seed values
|
|
250
|
+
do not yield equal reference draws across languages. The distributed tests
|
|
251
|
+
instead share fixed contributions, paths, directions and reference draws.
|
|
252
|
+
Record the package, NumPy and SciPy versions along with the seed and grid for
|
|
253
|
+
reproducibility. Bitwise agreement across different numerical-library versions
|
|
254
|
+
is not promised.
|
|
255
|
+
|
|
256
|
+
## API overview
|
|
257
|
+
|
|
258
|
+
| Entry point | Purpose |
|
|
259
|
+
|---|---|
|
|
260
|
+
| `mean(data, names=None)` | Sample-mean estimate and influence path |
|
|
261
|
+
| `ols(y, x, intercept=True, names=None)` | Full OLS coefficient inference |
|
|
262
|
+
| `from_influence(estimate, psi, profile=None, names=None)` | Other estimators |
|
|
263
|
+
| `InfluencePath(psi, profile=None)` | Inspect centered partial sums |
|
|
264
|
+
| `IncrementHull(path)` | Gauge, support and vertices |
|
|
265
|
+
| `simulate_reference(q, n, profile=None, draws=..., seed=...)` | Matched-grid reference |
|
|
266
|
+
| `Reference(q, nodes, draws, batches=20)` | Import known reference draws |
|
|
267
|
+
| `Reference.continuous()` | Scalar continuous-path reference |
|
|
268
|
+
| `fit.test(null, reference=ref)` | Test and Monte Carlo uncertainty |
|
|
269
|
+
| `fit.region(reference=ref)` | Joint confidence region |
|
|
270
|
+
| `fit.confint(reference=ref)` | Simultaneous coordinate intervals |
|
|
271
|
+
| `fit.contrast(A, reference=ref)` | Simultaneous contrast intervals |
|
|
272
|
+
| `fit.transform(A, offset=None)` | Linear reparameterization or lower-dimensional target |
|
|
273
|
+
|
|
274
|
+
Arguments after the first required arguments are generally keyword-only; see
|
|
275
|
+
`help(aersn.ols)` and other docstrings for exact signatures.
|