westata 0.0.1__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.
Files changed (141) hide show
  1. westata-0.0.1/CHANGELOG.md +101 -0
  2. westata-0.0.1/LICENSE +21 -0
  3. westata-0.0.1/MANIFEST.in +9 -0
  4. westata-0.0.1/PKG-INFO +354 -0
  5. westata-0.0.1/README.md +304 -0
  6. westata-0.0.1/README.zh-CN.md +232 -0
  7. westata-0.0.1/docs/GUIDE.md +581 -0
  8. westata-0.0.1/docs/GUIDE.zh-CN.md +577 -0
  9. westata-0.0.1/docs/build_doc_data.py +158 -0
  10. westata-0.0.1/docs/collect_comparison.py +295 -0
  11. westata-0.0.1/docs/comparison.json +753 -0
  12. westata-0.0.1/docs/data/auto.csv +75 -0
  13. westata-0.0.1/docs/data/panel.csv +1601 -0
  14. westata-0.0.1/docs/data/sim.csv +601 -0
  15. westata-0.0.1/docs/gen_guide_zh.py +444 -0
  16. westata-0.0.1/docs/gen_readme_zh.py +286 -0
  17. westata-0.0.1/docs/generate_guide.py +457 -0
  18. westata-0.0.1/docs/verify_guide.py +90 -0
  19. westata-0.0.1/pyproject.toml +59 -0
  20. westata-0.0.1/setup.cfg +4 -0
  21. westata-0.0.1/src/westata/__init__.py +223 -0
  22. westata-0.0.1/src/westata/conformance/__init__.py +37 -0
  23. westata-0.0.1/src/westata/conformance/oracles/__init__.py +7 -0
  24. westata-0.0.1/src/westata/conformance/oracles/invariants.py +124 -0
  25. westata-0.0.1/src/westata/conformance/oracles/stata.py +270 -0
  26. westata-0.0.1/src/westata/conformance/registry.py +180 -0
  27. westata-0.0.1/src/westata/conformance/tolerances.py +91 -0
  28. westata-0.0.1/src/westata/data/__init__.py +7 -0
  29. westata-0.0.1/src/westata/data/weights.py +134 -0
  30. westata-0.0.1/src/westata/errors.py +45 -0
  31. westata-0.0.1/src/westata/estimators/__init__.py +164 -0
  32. westata-0.0.1/src/westata/estimators/counts.py +1040 -0
  33. westata-0.0.1/src/westata/estimators/did.py +840 -0
  34. westata-0.0.1/src/westata/estimators/discrete.py +1616 -0
  35. westata-0.0.1/src/westata/estimators/hdfe.py +677 -0
  36. westata-0.0.1/src/westata/estimators/iv.py +1037 -0
  37. westata-0.0.1/src/westata/estimators/limited.py +2323 -0
  38. westata-0.0.1/src/westata/estimators/linear.py +349 -0
  39. westata-0.0.1/src/westata/estimators/nonlinear.py +1683 -0
  40. westata-0.0.1/src/westata/estimators/panel.py +1111 -0
  41. westata-0.0.1/src/westata/estimators/panel_dynamic.py +496 -0
  42. westata-0.0.1/src/westata/estimators/panel_iv.py +2224 -0
  43. westata-0.0.1/src/westata/estimators/panel_nonlinear.py +2073 -0
  44. westata-0.0.1/src/westata/estimators/quantile.py +1075 -0
  45. westata-0.0.1/src/westata/estimators/quantile_boot.py +348 -0
  46. westata-0.0.1/src/westata/estimators/resample.py +698 -0
  47. westata-0.0.1/src/westata/estimators/survey.py +1296 -0
  48. westata-0.0.1/src/westata/estimators/systems.py +1208 -0
  49. westata-0.0.1/src/westata/estimators/tobit.py +611 -0
  50. westata-0.0.1/src/westata/estimators/treatreg_effects.py +1304 -0
  51. westata-0.0.1/src/westata/estimators/tseries.py +1607 -0
  52. westata-0.0.1/src/westata/estimators/xtunitroot_ips.py +290 -0
  53. westata-0.0.1/src/westata/estimators/xtunitroot_llc.py +405 -0
  54. westata-0.0.1/src/westata/expressions/__init__.py +21 -0
  55. westata-0.0.1/src/westata/expressions/explist.py +536 -0
  56. westata-0.0.1/src/westata/formula/__init__.py +11 -0
  57. westata-0.0.1/src/westata/formula/factor.py +114 -0
  58. westata-0.0.1/src/westata/postestimation/__init__.py +56 -0
  59. westata-0.0.1/src/westata/postestimation/api.py +447 -0
  60. westata-0.0.1/src/westata/postestimation/contrast.py +582 -0
  61. westata-0.0.1/src/westata/postestimation/lincom.py +133 -0
  62. westata-0.0.1/src/westata/postestimation/margins.py +805 -0
  63. westata-0.0.1/src/westata/postestimation/modeltests.py +431 -0
  64. westata-0.0.1/src/westata/postestimation/nonlinear.py +746 -0
  65. westata-0.0.1/src/westata/postestimation/predict.py +823 -0
  66. westata-0.0.1/src/westata/postestimation/tests.py +210 -0
  67. westata-0.0.1/src/westata/protocols.py +56 -0
  68. westata-0.0.1/src/westata/random/__init__.py +9 -0
  69. westata-0.0.1/src/westata/random/stata_rng.py +95 -0
  70. westata-0.0.1/src/westata/report/__init__.py +7 -0
  71. westata-0.0.1/src/westata/report/etable.py +219 -0
  72. westata-0.0.1/src/westata/results/__init__.py +14 -0
  73. westata-0.0.1/src/westata/results/base.py +464 -0
  74. westata-0.0.1/src/westata/results/stata_return.py +67 -0
  75. westata-0.0.1/src/westata/results/summary.py +323 -0
  76. westata-0.0.1/src/westata/vce/__init__.py +36 -0
  77. westata-0.0.1/src/westata/vce/cluster.py +100 -0
  78. westata-0.0.1/src/westata/vce/dispatch.py +199 -0
  79. westata-0.0.1/src/westata/vce/robust.py +131 -0
  80. westata-0.0.1/src/westata/vce/ssc.py +75 -0
  81. westata-0.0.1/src/westata.egg-info/PKG-INFO +354 -0
  82. westata-0.0.1/src/westata.egg-info/SOURCES.txt +139 -0
  83. westata-0.0.1/src/westata.egg-info/dependency_links.txt +1 -0
  84. westata-0.0.1/src/westata.egg-info/requires.txt +14 -0
  85. westata-0.0.1/src/westata.egg-info/top_level.txt +1 -0
  86. westata-0.0.1/tests/test_api_smoke.py +111 -0
  87. westata-0.0.1/tests/test_cluster_df.py +141 -0
  88. westata-0.0.1/tests/test_contrast.py +634 -0
  89. westata-0.0.1/tests/test_counts.py +776 -0
  90. westata-0.0.1/tests/test_did.py +630 -0
  91. westata-0.0.1/tests/test_discrete.py +480 -0
  92. westata-0.0.1/tests/test_docs_zh.py +257 -0
  93. westata-0.0.1/tests/test_etable.py +152 -0
  94. westata-0.0.1/tests/test_explist.py +196 -0
  95. westata-0.0.1/tests/test_formula.py +196 -0
  96. westata-0.0.1/tests/test_freq_weights.py +123 -0
  97. westata-0.0.1/tests/test_fweight_expansion.py +85 -0
  98. westata-0.0.1/tests/test_guide.py +178 -0
  99. westata-0.0.1/tests/test_hdfe.py +430 -0
  100. westata-0.0.1/tests/test_heckman_covariance.py +144 -0
  101. westata-0.0.1/tests/test_iv.py +686 -0
  102. westata-0.0.1/tests/test_lad_exactness.py +179 -0
  103. westata-0.0.1/tests/test_limited.py +770 -0
  104. westata-0.0.1/tests/test_margins.py +418 -0
  105. westata-0.0.1/tests/test_modeltests.py +669 -0
  106. westata-0.0.1/tests/test_nb1.py +140 -0
  107. westata-0.0.1/tests/test_nb2_information.py +148 -0
  108. westata-0.0.1/tests/test_nl.py +988 -0
  109. westata-0.0.1/tests/test_nonlinear.py +396 -0
  110. westata-0.0.1/tests/test_ols.py +306 -0
  111. westata-0.0.1/tests/test_ols_weights.py +114 -0
  112. westata-0.0.1/tests/test_packaging_hygiene.py +126 -0
  113. westata-0.0.1/tests/test_panel.py +822 -0
  114. westata-0.0.1/tests/test_panel_dynamic.py +206 -0
  115. westata-0.0.1/tests/test_panel_iv.py +924 -0
  116. westata-0.0.1/tests/test_panel_nonlinear.py +752 -0
  117. westata-0.0.1/tests/test_postestimation.py +382 -0
  118. westata-0.0.1/tests/test_predict_se.py +549 -0
  119. westata-0.0.1/tests/test_quantile.py +841 -0
  120. westata-0.0.1/tests/test_quantile_boot.py +167 -0
  121. westata-0.0.1/tests/test_readme_examples.py +111 -0
  122. westata-0.0.1/tests/test_resample.py +213 -0
  123. westata-0.0.1/tests/test_results.py +244 -0
  124. westata-0.0.1/tests/test_retest_sweep.py +180 -0
  125. westata-0.0.1/tests/test_ssc.py +155 -0
  126. westata-0.0.1/tests/test_stata_rng.py +136 -0
  127. westata-0.0.1/tests/test_sureg_duplicate_names.py +91 -0
  128. westata-0.0.1/tests/test_sureg_robust.py +89 -0
  129. westata-0.0.1/tests/test_survey.py +692 -0
  130. westata-0.0.1/tests/test_systems.py +755 -0
  131. westata-0.0.1/tests/test_teffects.py +736 -0
  132. westata-0.0.1/tests/test_teffects_did.py +237 -0
  133. westata-0.0.1/tests/test_tobit.py +389 -0
  134. westata-0.0.1/tests/test_tseries.py +605 -0
  135. westata-0.0.1/tests/test_two_step_vce.py +144 -0
  136. westata-0.0.1/tests/test_vce_cluster.py +238 -0
  137. westata-0.0.1/tests/test_vce_dispatch.py +160 -0
  138. westata-0.0.1/tests/test_vce_robust.py +143 -0
  139. westata-0.0.1/tests/test_weights.py +74 -0
  140. westata-0.0.1/tests/test_xtunitroot_ips.py +184 -0
  141. westata-0.0.1/tests/test_xtunitroot_llc.py +236 -0
@@ -0,0 +1,101 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — with
6
+ the caveat that `0.0.x` explicitly carries **no stability promise**.
7
+
8
+ ---
9
+
10
+ ## [Unreleased]
11
+
12
+ Nothing yet.
13
+
14
+ ---
15
+
16
+ ## [0.0.1] — first release
17
+
18
+ The first tagged version. Everything below is the initial state of the library,
19
+ not a diff against an earlier release.
20
+
21
+ ### What this library is
22
+
23
+ `westata` reimplements a set of Stata estimation commands with a
24
+ statsmodels-style API, and checks each one against a real Stata installation.
25
+ The point is not "similar numbers" — it is a measured, reproducible claim about
26
+ which commands agree and how closely.
27
+
28
+ Verification: **StataNow 19.5 (MP)** via `pystata`. Captures are committed, so
29
+ the comparisons replay without a licence.
30
+
31
+ ### Coverage
32
+
33
+ | | |
34
+ |---|---|
35
+ | Registry entries | 87 |
36
+ | Tests | 1434 |
37
+ | Independent comparison points | 386 (against live Stata) |
38
+ | Evidence level E3 (machine precision) | 52 |
39
+ | Evidence level E2 (bounded, measured) | 26 |
40
+ | Evidence level E1 (deviates or uncovered) | 9 |
41
+
42
+ Domains: linear regression and IV, discrete choice and count models, panel
43
+ data, limited-dependent and quantile models, time series, systems of equations
44
+ and causal inference, and post-estimation.
45
+
46
+ ### Known limitations — READ THIS BEFORE RELYING ON A NUMBER
47
+
48
+ Nine registry entries measurably deviate on at least one invocation. Each is
49
+ listed with its worst measured relative difference and the reason, because a
50
+ number without a reason is not actionable.
51
+
52
+ | Command | Worst measured rel. diff. | Reason |
53
+ |---|---|---|
54
+ | `xtgls` | 3.4e+00 | **Only when the cross-equation Sigma is rank-deficient** (more equations than periods). No unique generalised inverse exists; Stata warns here too. Well-posed systems are at machine precision. |
55
+ | `teffects nnmatch` | 7.1e-02 | **Standard error only** — the point estimate is exact (3.6e-15). Relies on compiled Mata internally. |
56
+ | `frontier` | 4.3e-02 | `distribution(exponential)` only, where `sigma_u` is nearly unidentified on the test data. The likelihood at Stata's estimates matches exactly. |
57
+ | `bsqreg` | 3.0e-02 | Bootstrap VCE. Coefficients are exact (3.4e-16); a resampling estimator has not converged at any finite replicate count. |
58
+ | `permute` | 1.2e-02 | Stata's shuffle order is not reproducible from its output. |
59
+ | `arima` | 5.5e-03 | Optimiser stopping point; our likelihood is *higher* than Stata's. |
60
+ | `rreg` | 1.0e-03 | Final biweight case weights. |
61
+ | `xtlogit, pa` | 1.1e-03 | GEE working-correlation handling. |
62
+ | `heckman` | 1.9e-05 | Two-step standard errors. The ML path is a separate and exact estimate. |
63
+
64
+ **One capability is absent rather than approximate:** `teffects nnmatch,
65
+ biasadj()` raises `UnsupportedError`.
66
+
67
+ Some of these gaps are provably irreducible rather than unfinished:
68
+
69
+ - `xtgls` with a rank-deficient Sigma has no unique answer to converge to.
70
+ - `teffects nnmatch` and parts of `qreg` call compiled Stata bytecode with no
71
+ readable source.
72
+ - `arima`, the count family and `frontier` stop where Stata stops; matching
73
+ Stata's stopping point would mean reporting a *worse* optimum than the one
74
+ found.
75
+ - A bootstrap VCE cannot match pointwise at any finite replicate count.
76
+
77
+ ### Dependencies
78
+
79
+ - Required: `numpy`, `pandas`, `scipy`, `statsmodels`, `patsy`, `pyfixest`
80
+ (HDFE), `linearmodels` (IV and panel), `pyyaml`.
81
+ The last three are **not optional** — the package does not import without
82
+ them. An earlier build declared them nowhere and installed cleanly while
83
+ raising `ModuleNotFoundError` on import.
84
+ - Optional: `arch` for GARCH and the Phillips–Perron test. Install with
85
+ `pip install "westata[arch]"`. Without it those commands raise
86
+ `UnsupportedError` naming the install command, and their tests skip rather
87
+ than fail.
88
+
89
+ Python 3.10 or newer.
90
+
91
+ ### Notes on this release
92
+
93
+ - Version `0.0.1` is a placeholder in the SemVer sense: the API may change
94
+ without notice. It exists so the packaging pipeline can be exercised
95
+ end to end.
96
+ - The evidence levels are the project's central artefact. They are stored in
97
+ `tests/conformance/registry.yml`, graded by an independent sweep in `retest/`,
98
+ and a registry level that the measurement does not support causes the sweep to
99
+ fail. A false claim is treated as a defect.
100
+ - `retest/` requires a Stata licence and does not run in CI. CI runs the test
101
+ suite, which replays committed captures and needs no licence.
westata-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 westata contributors
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.
@@ -0,0 +1,9 @@
1
+ include CHANGELOG.md
2
+ include LICENSE
3
+ include README.md
4
+ include README.zh-CN.md
5
+ include docs/GUIDE.md
6
+ include docs/GUIDE.zh-CN.md
7
+ include docs/comparison.json
8
+ include docs/data/*.csv
9
+ recursive-include docs *.py
westata-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,354 @@
1
+ Metadata-Version: 2.4
2
+ Name: westata
3
+ Version: 0.0.1
4
+ Summary: Stata-conformant econometrics with a statsmodels-style API
5
+ License: MIT License
6
+
7
+ Copyright (c) 2026 westata contributors
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Classifier: Development Status :: 3 - Alpha
28
+ Classifier: Intended Audience :: Science/Research
29
+ Classifier: Programming Language :: Python :: 3
30
+ Classifier: Programming Language :: Python :: 3.10
31
+ Classifier: Programming Language :: Python :: 3.11
32
+ Classifier: Programming Language :: Python :: 3.12
33
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
34
+ Requires-Python: >=3.10
35
+ Description-Content-Type: text/markdown
36
+ License-File: LICENSE
37
+ Requires-Dist: numpy>=1.24
38
+ Requires-Dist: pandas>=2.0
39
+ Requires-Dist: scipy>=1.10
40
+ Requires-Dist: statsmodels>=0.14
41
+ Requires-Dist: patsy>=0.5
42
+ Requires-Dist: pyfixest>=0.30
43
+ Requires-Dist: linearmodels>=6.0
44
+ Requires-Dist: pyyaml>=6
45
+ Provides-Extra: test
46
+ Requires-Dist: pytest>=7; extra == "test"
47
+ Provides-Extra: arch
48
+ Requires-Dist: arch>=6.0; extra == "arch"
49
+ Dynamic: license-file
50
+
51
+ # westata
52
+
53
+ Stata-conformant econometrics with a statsmodels-style API.
54
+
55
+ > **Read this before relying on a number.** `westata` is a personal research
56
+ > library at version 0.0.1. 52 of its 87 commands match Stata to machine
57
+ > precision, but **9 do not**, and one feature is missing. The measured
58
+ > deviation for each is listed below. They are graded, not hidden, and the
59
+ > grading is reproducible from this repository.
60
+ >
61
+ > | Command | Worst measured relative difference | Why |
62
+ > |---|---|---|
63
+ > | `xtgls` | 3.4e+00 | **Only when the cross-equation Sigma is rank-deficient** (more equations than periods). No unique generalised inverse exists; Stata warns here too. On well-posed systems this is machine precision. |
64
+ > | `teffects nnmatch` | 7.1e-02 | **Standard error only** — the point estimate is exact (3.6e-15). Uses compiled Mata internally. |
65
+ > | `frontier` | 4.3e-02 | `distribution(exponential)` only, where `sigma_u` is nearly unidentified on the test data. The likelihood at Stata's estimates matches exactly. |
66
+ > | `bsqreg` | 3.0e-02 | Bootstrap VCE. Coefficients are exact (3.4e-16); a resampling estimator has not converged at any finite replicate count. |
67
+ > | `permute` | 1.2e-02 | Stata's shuffle order for the permutation draws is not reproducible from its output. |
68
+ > | `arima` | 5.5e-03 | Optimiser stopping point; our likelihood is *higher* than Stata's. |
69
+ > | `rreg` | 1.0e-03 | Final biweight case weights. |
70
+ > | `xtlogit, pa` | 1.1e-03 | GEE working-correlation handling. |
71
+ > | `heckman` | 1.9e-05 | Two-step standard errors. The ML path added later is a separate, exact estimate. |
72
+ >
73
+ > One capability is **absent rather than approximate**: `teffects nnmatch,
74
+ > biasadj()` raises `UnsupportedError`.
75
+ >
76
+ > Each figure is the worst case across the invocations an independent
77
+ > 386-point sweep exercised against live Stata. Most of the library is far
78
+ > better than these bounds — 250 of 386 comparison points are at machine
79
+ > precision — but these are the numbers that would bite you, so they come
80
+ > first.
81
+
82
+ `westata` is a personal research library that answers one question: *if I run
83
+ this in Python, do I get the same numbers Stata would print?*
84
+
85
+ - **API follows statsmodels** — `Model.from_formula(...).fit()`, `params`,
86
+ `bse`, `pvalues`, `conf_int()`, `cov_params()`. Results are duck-typed, so
87
+ existing statsmodels-style downstream code keeps working.
88
+ - **Numerics follow Stata** — small-sample corrections, weight semantics,
89
+ factor-variable coding, degrees of freedom, and degrees-of-freedom splits are
90
+ implemented per the corresponding Stata manual entry.
91
+ - **Claims are auditable** — every command carries an evidence level in
92
+ `tests/conformance/registry.yml`. Nothing claims more than it can show.
93
+
94
+ ## Status
95
+
96
+ | | |
97
+ |---|---|
98
+ | Tests | **1448 passing** |
99
+ | Commands covered | 87 registry entries |
100
+ | Evidence | **52 × E3**, 26 × E2, 9 × E1 |
101
+ | Independent sweep | 386 comparison points against live Stata (`retest/`) |
102
+
103
+ Verified against **StataNow 19.5 (MP)** through `pystata`; the captures are
104
+ committed so the comparison replays without a licence.
105
+
106
+ ## Install
107
+
108
+ `westata` is **not on PyPI yet**. Install it from the repository:
109
+
110
+ ```bash
111
+ git clone https://github.com/bigchill-z/westata.git
112
+ cd westata
113
+ python -m venv .venv
114
+ .venv\Scripts\python.exe -m pip install -e ".[test]"
115
+ ```
116
+
117
+ On Linux or macOS the last line is `./.venv/bin/python -m pip install -e ".[test]"`.
118
+
119
+ Requires Python ≥ 3.10. `numpy`, `pandas`, `scipy`, `statsmodels`, `patsy`,
120
+ `pyfixest` (HDFE), `linearmodels` (IV, panel) and `pyyaml` come in
121
+ automatically — the package does not import without the last three.
122
+
123
+ `arch` is optional and only needed for GARCH, EGARCH and the Phillips–Perron
124
+ test. Install it alongside the package:
125
+
126
+ ```bash
127
+ .venv\Scripts\python.exe -m pip install -e ".[test,arch]"
128
+ ```
129
+
130
+ Without it those commands raise an `UnsupportedError` naming the missing extra,
131
+ and their tests skip rather than fail. Once the package is on PyPI, the
132
+ equivalent commands will be `pip install westata` and `pip install
133
+ "westata[arch]"`.
134
+
135
+ ## Quick start
136
+
137
+ Every line below is executed as written against `tests/data/auto.dta`.
138
+
139
+ ```python
140
+ import pandas as pd
141
+ import westata as ws
142
+
143
+ df = pd.read_stata("tests/data/auto.dta")
144
+
145
+ # OLS with a factor variable and clustered standard errors (regress)
146
+ res = ws.OLS.from_formula("mpg ~ weight + C(foreign)", df).fit(
147
+ cov_type="cluster", cov_kwds={"groups": df["foreign"]},
148
+ )
149
+ print(res.summary(style="stata")) # Stata layout
150
+ ```
151
+
152
+ Coefficient names keep the formula's spelling, so a factor level appears as
153
+ `C(foreign)[T.Foreign]`. Use that exact string:
154
+
155
+ ```python
156
+ res.test("weight") # single restriction
157
+ res.test("weight", "C(foreign)[T.Foreign]") # joint test of both
158
+ res.test("weight = 0") # same, as an equation
159
+ res.lincom("weight + 1000*C(foreign)[T.Foreign]") # linear combination
160
+ ```
161
+
162
+ `test` takes one specification per argument; a comma inside a single string is not a separator.
163
+
164
+ Other models, on the same data:
165
+
166
+ ```python
167
+ # Quantile regression (qreg) — median
168
+ ws.QuantileRegression.from_formula("mpg ~ weight + C(foreign)", df).fit()
169
+
170
+ # Robust regression (rreg)
171
+ ws.RobustRegression.from_formula("mpg ~ weight", df).fit()
172
+
173
+ # Systems of equations (sureg): one list entry per equation
174
+ ws.Sureg.from_formula(["price ~ weight + mpg", "mpg ~ weight + foreign"], df).fit()
175
+ ws.Sureg.from_formula(
176
+ ["price ~ weight + mpg", "mpg ~ weight + foreign"], df
177
+ ).fit(cov_type="robust") # robust VCE
178
+
179
+ # Censored regression (tobit). `lower` is a from_formula argument, not a fit one.
180
+ # `rep78` has missing values, which are dropped.
181
+ ws.Tobit.from_formula("rep78 ~ weight + mpg", df, lower=0).fit()
182
+ ```
183
+
184
+ A publication-style table:
185
+
186
+ ```python
187
+ base = ws.OLS.from_formula("mpg ~ weight", df).fit()
188
+ full = ws.OLS.from_formula("mpg ~ weight + C(foreign)", df).fit()
189
+ print(ws.etable([base, full], model_names=["Base", "Full"]))
190
+ ```
191
+
192
+ ## Command coverage
193
+
194
+ | Family | westata | Stata |
195
+ |---|---|---|
196
+ | Linear | `OLS` | `regress` (4 weight types, `i.` factors) |
197
+ | IV | `IV2SLS` / `IVLIML` / `IVGMM` | `ivregress 2sls/liml/gmm` |
198
+ | HDFE | `FEOLS` | `reghdfe` / `areg` |
199
+ | Panel | `PanelOLS` / `RandomEffects` / `BetweenOLS` / `FirstDifferenceOLS` | `xtreg fe/re/be/fd` |
200
+ | Binary | `Logit` / `Probit` | `logit` / `probit` |
201
+ | Count | `Poisson` / `NegativeBinomial` | `poisson` / `nbreg` |
202
+ | Ordered | `OrderedLogit` / `OrderedProbit` | `ologit` / `oprobit` |
203
+ | Censored | `Tobit` (self-implemented) | `tobit` |
204
+ | Time series | `ARIMA` / `VAR` / `VECM` / `GARCH` / `unitroot` | `arima` / `var` / `vec` / `arch` / `dfuller` |
205
+ | Postestimation | `test` / `lincom` / `margins` / `predict` | same names |
206
+ | Reporting | `etable` | `esttab` |
207
+
208
+ ## Weight semantics
209
+
210
+ Following Stata `[U] 20.24`:
211
+
212
+ | Type | Point estimate | Variance | Effective N |
213
+ |---|---|---|---|
214
+ | `fweight` | literal replication | conventional | `sum(w)` |
215
+ | `aweight` | normalized to `sum(w) = N` | conventional | `N` |
216
+ | `pweight` | same as aweight | **robust (HC1)** | `N` |
217
+ | `iweight` | not normalized | conventional | `sum(w)` |
218
+
219
+ `weight_type` is mandatory whenever weights are supplied — westata never
220
+ guesses, because guessing silently changes the VCE.
221
+
222
+ ## Evidence levels
223
+
224
+ | Level | Meaning | Count |
225
+ |---|---|---|
226
+ | E3 | Agrees with full-precision `e(b)`/`e(V)` from real Stata, on every invocation the independent sweep exercised | 52 |
227
+ | E2 | Approximate, or exact only on a subset of invocations: real but bounded differences | 26 |
228
+ | E1 | Materially differs on at least one measured invocation, or not covered by the sweep | 9 |
229
+
230
+ **These levels come from the independent 386-point sweep, not from the library's own
231
+ tests.** That distinction matters: the library's tests passed at 1247 while the sweep
232
+ found 24 defects, because a test written from the same understanding as the code cannot
233
+ detect a misunderstanding shared by both.
234
+
235
+ Measured relative agreement with StataNow 19.5:
236
+
237
+ | Command | Parameters | Standard errors |
238
+ |---|---|---|
239
+ | `regress` | 5.1e-15 | 6.8e-16 |
240
+ | `regress, vce(robust)` | 5.1e-15 | 4.6e-15 |
241
+ | `regress, cluster` | 5.1e-15 | 7.1e-15 |
242
+ | `regress, i.factor` | 3.7e-14 | 5.1e-16 |
243
+ | `ivregress 2sls` | 7.8e-15 | 7.5e-15 |
244
+ | `ivregress liml` | 3.7e-16 | 3.7e-16 |
245
+ | `xtreg, fe` | 1.8e-16 | 1.9e-16 |
246
+ | `xtreg, re` | 6.8e-16 | 1.0e-15 |
247
+ | `xtreg, be` | 3.6e-16 | 1.7e-16 |
248
+ | `xtreg, fd` | not comparable (command removed) | — |
249
+ | `areg` | 4.6e-16 | 1.8e-16 |
250
+ | `reghdfe` (1 FE) | 4.5e-16 | 3.3e-16 |
251
+ | `reghdfe` (2 FE) | 4.4e-16 | 1.9e-16 |
252
+ | `reghdfe` (2 FE, cluster) | 4.4e-16 | 2.0e-16 |
253
+ | `var` | 2.9e-15 | — |
254
+ | `poisson` | 3.9e-13 | 5.8e-14 |
255
+ | `logit` | ~1e-7 | ~1e-7 |
256
+ | `probit` | 1.5e-7 | — |
257
+ | `tobit` | 8.5e-8 | 6.4e-7 |
258
+ | `margins, dydx` | 1.6e-7 | 5.2e-6 |
259
+ | `ologit` / `oprobit` | ~2e-5 | — |
260
+ | `nbreg` | 1.7e-5 | 9.1e-7 |
261
+ | `arima` | 4.8e-5 | 8.2e-5 |
262
+ | `predict` (OLS xb) | 1.6e-15 | — |
263
+ | `predict` (logit pr) | 3.1e-11 | — |
264
+ | `predict` (tobit e/ystar) | 5.8e-10 | — |
265
+ | `etable` vs `esttab` | 0.0 | — |
266
+ | `vecrank` (all lag orders) | 0.0 (4 dp) | — |
267
+ | `dfuller` | 2.3e-4 | — |
268
+ | `ivregress gmm` | 4.9e-16 | 6.9e-18 |
269
+ | `arch` | 1.4e-04 | z values not comparable |
270
+
271
+ The linear, panel-linear, multi-equation and complex-survey families agree to
272
+ machine precision (1e-15). **Non-linear maximum-likelihood models are where the
273
+ differences live**, and almost always in the second-order quantities rather than the
274
+ coefficients: an exact point estimate with a standard error that is 5% off still gives a
275
+ wrong z, a wrong p, and a wrong confidence interval.
276
+
277
+ What was measured wrong, and then fixed:
278
+
279
+ - `ologit`/`oprobit` `e(V)`: a numpy view alias let `exp()` act on the cut points, and the
280
+ slope-by-cut block was never transformed — `e(V)` off by 20x, all cut-point z and CI wrong.
281
+ - `nl` covariance: `pinv` truncation made standard errors wrong by **6–7 orders of
282
+ magnitude**, so every t statistic was inflated.
283
+ - `xtreg, fe vce(robust)` was mapped to HC1; Stata defines it as `vce(cluster panelvar)`
284
+ (verified: the two are identical in Stata, `max|V_robust − V_cluster| = 0`). The constant's
285
+ SE was 18x off.
286
+ - `tobit` `se(/sigma)` lacked the delta-method Jacobian; `margins` used a fixed numerical
287
+ step; `podiscrete` VCEs silently accepted and ignored `vce(robust)`.
288
+
289
+ What remains approximate is listed per command in
290
+ [`docs/USING_WITH_STATA.md`](docs/USING_WITH_STATA.md), which is organised around the
291
+ question a user actually has: **can I quote this number as Stata's?**
292
+
293
+ Two commands could not be compared at all because StataNow 19 removed them:
294
+ `xtreg, fd` (r(198)) and `kpss` (r(199)). Their westata counterparts are
295
+ validated by other means.
296
+
297
+ ## Design notes worth knowing
298
+
299
+ Things that bit during development, documented so they do not bite again:
300
+
301
+ - **`df_resid` vs `e(df_r)` are different quantities.** Under clustering, t
302
+ tests use `G − 1` while Stata's `e(df_r)` stays `N − K`. Conflating them
303
+ silently gives wrong p-values.
304
+ - **Frequency weights must equal the expanded-data fit.** Stata's own stated
305
+ invariant. Getting the robust meat's weight form wrong inflates fweighted
306
+ standard errors by up to 51%.
307
+ - **`margins` differentiates the response, not the linear index.** For a logit,
308
+ `mean(p(1-p))·b` — differentiating `xb` returns the coefficient, which was
309
+ 4.38× too large in the first implementation.
310
+ - **`PanelOLS.from_formula` ignores `entity_effects`.** Passing it through
311
+ `fit()` silently produces pooled OLS. The model must be constructed directly.
312
+ - **linearmodels' clustered VCE needs `auto_df=False, group_debias=True`** to
313
+ reproduce Stata's `G/(G-1)·(N-1)/(N-K)`; the default is ~11% off.
314
+ - **`ivregress` reports z / Wald chi2**, not t / F. OLS and panel use t.
315
+
316
+ ## Layout
317
+
318
+ ```
319
+ src/westata/
320
+ data/ weight normalization, panel/time-series metadata
321
+ formula/ patsy design matrices, C(x, base=)
322
+ vce/ HC0-HC3, CR1, ssc
323
+ estimators/ linear, iv, hdfe, panel, discrete, tobit, tseries
324
+ results/ StataResults, E(RETURN), Stata/statsmodels summaries
325
+ postestimation/ test, lincom, margins, predict
326
+ report/ etable, LaTeX/Markdown renderers
327
+ conformance/ evidence registry, tolerances, invariants
328
+ tests/conformance/ registry.yml + published-output fixtures
329
+ docs/ design spec and implementation plans
330
+ ```
331
+
332
+ ## Documentation
333
+
334
+ - **Comparing westata with Stata**: [`docs/USING_WITH_STATA.md`](docs/USING_WITH_STATA.md) —
335
+ which quantities are interchangeable and which are not, for the four E2 commands.
336
+ - Evidence registry: [`tests/conformance/registry.yml`](tests/conformance/registry.yml) —
337
+ per-command level, measured gap, and reasoning.
338
+ - Known gaps: [`docs/GAP_REGISTER.md`](docs/GAP_REGISTER.md) — what is deliberately
339
+ not implemented, and why.
340
+ - **Independent sweep**: [retest/README.md](retest/README.md) — the 386-point
341
+ check against live Stata, and how to re-run it. This is where the evidence
342
+ levels come from.
343
+ - Design spec: [`docs/superpowers/specs/2026-10-22-westata-design.md`](docs/superpowers/specs/2026-10-22-westata-design.md)
344
+
345
+ ## Known gaps
346
+
347
+ Not implemented, and they raise instead of approximating: Mata, the graphics
348
+ system, data management, SEM/GSEM, survival analysis, Bayesian estimation, `mi`,
349
+ `xtabond2`, `csdid`, `rdrobust`, `synth`, general `gmm`, `nlsur`, multi-way
350
+ clustering (CGM), `vecrank`'s fixed-N exact critical values, and Stata's
351
+ arbitrary `r()` statistics outside the `[U] exp_list` grammar.
352
+
353
+ `docs/GAP_REGISTER.md` lists every one with its reason. Those are boundary
354
+ statements, not a to-do list.