stata-cli 0.2.2__tar.gz → 0.4.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.
- {stata_cli-0.2.2 → stata_cli-0.4.0}/PKG-INFO +103 -6
- {stata_cli-0.2.2 → stata_cli-0.4.0}/README.md +101 -4
- {stata_cli-0.2.2 → stata_cli-0.4.0}/pyproject.toml +5 -2
- stata_cli-0.4.0/src/stata_cli/__init__.py +1 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli/daemon.py +31 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli/engine.py +181 -8
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli/main.py +202 -5
- stata_cli-0.4.0/src/stata_cli/skill_registry.py +171 -0
- stata_cli-0.4.0/src/stata_cli/skills/overview.md +196 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/asdoc.md +357 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/binsreg.md +358 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/coefplot.md +397 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/data-manipulation.md +407 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/diagnostics.md +621 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/did.md +583 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/estout.md +676 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/event-study.md +1032 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/graph-schemes.md +633 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/ivreg2.md +387 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/nprobust.md +447 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/outreg2.md +424 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/package-management.md +319 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/psmatch2.md +658 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/rdrobust.md +498 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/reghdfe.md +372 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/synth.md +873 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/tabout.md +533 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/winsor.md +284 -0
- stata_cli-0.4.0/src/stata_cli/skills/packages/xtabond2.md +544 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/advanced-programming.md +506 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/basics-getting-started.md +237 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/bootstrap-simulation.md +327 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/data-import-export.md +282 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/data-management.md +426 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/date-time-functions.md +282 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/descriptive-statistics.md +268 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/difference-in-differences.md +750 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/external-tools-integration.md +966 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/gmm-estimation.md +367 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/graphics.md +344 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/limited-dependent-variables.md +289 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/linear-regression.md +398 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/machine-learning.md +511 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/mata-data-access.md +370 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/mata-introduction.md +313 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/mata-matrix-operations.md +305 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/mata-programming.md +400 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/matching-methods.md +742 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/mathematical-functions.md +269 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/maximum-likelihood.md +749 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/missing-data-handling.md +712 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/nonparametric-methods.md +478 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/panel-data.md +294 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/programming-basics.md +440 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/regression-discontinuity.md +486 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/sample-selection.md +670 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/sem-factor-analysis.md +576 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/spatial-analysis.md +766 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/string-functions.md +318 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/survey-data-analysis.md +595 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/survival-analysis.md +466 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/tables-reporting.md +973 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/time-series.md +345 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/treatment-effects.md +804 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/variables-operators.md +206 -0
- stata_cli-0.4.0/src/stata_cli/skills/references/workflow-best-practices.md +1176 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli/utils.py +1 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli.egg-info/PKG-INFO +103 -6
- stata_cli-0.4.0/src/stata_cli.egg-info/SOURCES.txt +76 -0
- stata_cli-0.2.2/src/stata_cli/__init__.py +0 -1
- stata_cli-0.2.2/src/stata_cli.egg-info/SOURCES.txt +0 -17
- {stata_cli-0.2.2 → stata_cli-0.4.0}/setup.cfg +0 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli/__main__.py +0 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli/graph_artifacts.py +0 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli/output_filter.py +0 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli/smcl_parser.py +0 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli.egg-info/dependency_links.txt +0 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli.egg-info/entry_points.txt +0 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli.egg-info/requires.txt +0 -0
- {stata_cli-0.2.2 → stata_cli-0.4.0}/src/stata_cli.egg-info/top_level.txt +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stata-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Command-line interface for running Stata commands via PyStata
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: stata,cli,statistics,data-science
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: click>=8.0
|
|
10
10
|
Provides-Extra: data
|
|
@@ -13,8 +13,12 @@ Requires-Dist: pandas; extra == "data"
|
|
|
13
13
|
|
|
14
14
|
# stata-cli
|
|
15
15
|
|
|
16
|
+
> **Stata CLI Is All Reg Monkeys Need**
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
16
20
|
[](https://opensource.org/licenses/MIT)
|
|
17
|
-
[](https://www.python.org/)
|
|
18
22
|
[](https://www.npmjs.com/package/stata-cli)
|
|
19
23
|
|
|
20
24
|
[中文版](README.zh.md) | [English](README.md)
|
|
@@ -39,18 +43,25 @@ A command-line interface for [Stata](https://www.stata.com/) via PyStata — bui
|
|
|
39
43
|
| **Run Code** | Execute inline Stata code, multi-line blocks, or pipe from stdin |
|
|
40
44
|
| **Do Files** | Run `.do` files with `///` line continuation support and graph auto-naming |
|
|
41
45
|
| **Data Viewer** | View current dataset as JSON with `if`-condition filtering and row limits |
|
|
46
|
+
| **Variable Metadata** | Inspect variable names, types, formats, and labels via `vars` |
|
|
47
|
+
| **Stored Results** | Retrieve r(), e(), s() results as structured JSON via `return` |
|
|
48
|
+
| **Matrix Access** | Read Stata matrices (e.g. `e(b)`, `e(V)`) as JSON via `matrix` |
|
|
49
|
+
| **Value Labels** | List and inspect value labels via `labels` |
|
|
50
|
+
| **Macro Access** | Get/set Stata macros including `c()`, `e()`, `r()` system macros |
|
|
51
|
+
| **Frame Management** | List Stata frames and current working frame via `frame` |
|
|
42
52
|
| **Help System** | Browse Stata help topics with SMCL-to-plain-text conversion |
|
|
43
|
-
| **Graph Export** | Auto-detect and export graphs as PNG to `~/.stata-cli/graphs/` |
|
|
53
|
+
| **Graph Export** | Auto-detect and export graphs as PNG/SVG/PDF to `~/.stata-cli/graphs/` |
|
|
44
54
|
| **Daemon Mode** | Persistent background process for sub-second execution via Unix socket |
|
|
45
|
-
| **Output Control** | Compact mode, JSON output, token limit management
|
|
55
|
+
| **Output Control** | Compact mode, JSON output, token limit management, log file output |
|
|
46
56
|
| **Interruption** | Send break signal to stop long-running commands |
|
|
57
|
+
| **Skill Library** | Built-in Stata reference with 57 topics: syntax, econometrics, causal inference, packages |
|
|
47
58
|
|
|
48
59
|
## Installation & Quick Start
|
|
49
60
|
|
|
50
61
|
### Requirements
|
|
51
62
|
|
|
52
63
|
- **Stata 17+** installed on your machine (provides the PyStata library)
|
|
53
|
-
- Python 3.
|
|
64
|
+
- Python 3.9+
|
|
54
65
|
|
|
55
66
|
### Quick Start (Human Users)
|
|
56
67
|
|
|
@@ -197,6 +208,72 @@ stata-cli detect
|
|
|
197
208
|
|
|
198
209
|
Prints the auto-detected Stata installation path.
|
|
199
210
|
|
|
211
|
+
### `return` — Retrieve Stored Results
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
stata-cli return r # r() results (after summarize, etc.)
|
|
215
|
+
stata-cli return e # e() results (after regress, etc.)
|
|
216
|
+
stata-cli return s # s() results
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Returns r(), e(), or s() stored results as structured JSON — scalars, macros, and matrix references.
|
|
220
|
+
|
|
221
|
+
### `vars` — Variable Metadata
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
stata-cli vars # all variables
|
|
225
|
+
stata-cli vars price mpg # specific variables
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Returns variable names, types, formats, and labels as JSON. More structured than `describe`.
|
|
229
|
+
|
|
230
|
+
### `matrix` — Read Stata Matrices
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
stata-cli matrix e(b) # coefficient vector
|
|
234
|
+
stata-cli matrix e(V) # variance-covariance matrix
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Returns matrix data, dimensions, and row/column names as JSON.
|
|
238
|
+
|
|
239
|
+
### `labels` — Value Labels
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
stata-cli labels # list all value label names
|
|
243
|
+
stata-cli labels origin # show value-label mapping
|
|
244
|
+
stata-cli labels --var foreign # show label attached to a variable
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### `macro` — Get/Set Macros
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
stata-cli macro get "c(current_date)"
|
|
251
|
+
stata-cli macro get "e(cmd)"
|
|
252
|
+
stata-cli macro set myvar "hello"
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Access Stata macros including system macros (`c()`, `e()`, `r()`).
|
|
256
|
+
|
|
257
|
+
### `frame` — List Frames
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
stata-cli frame
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Shows all Stata frames and the current working frame.
|
|
264
|
+
|
|
265
|
+
### `skill` — Stata Reference Library
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
stata-cli skill # overview: gotchas, patterns, topic routing table
|
|
269
|
+
stata-cli skill --list # list all 57 topics with descriptions
|
|
270
|
+
stata-cli skill regression # linear regression reference
|
|
271
|
+
stata-cli skill did # difference-in-differences guide
|
|
272
|
+
stata-cli skill reghdfe # reghdfe package guide
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Built-in reference library covering data management, econometrics, causal inference, graphics, Mata programming, and 20+ community packages. Aliases supported (e.g. `did` for `difference-in-differences`, `panel` for `panel-data`).
|
|
276
|
+
|
|
200
277
|
## Daemon Mode
|
|
201
278
|
|
|
202
279
|
The daemon keeps PyStata alive in the background — reduces execution time from **~2-3s to ~85ms** (35x speedup).
|
|
@@ -234,6 +311,8 @@ The daemon auto-shuts down after 1 hour of inactivity (configurable with `--idle
|
|
|
234
311
|
| `--max-tokens N` | Max output tokens (0=unlimited) | 0 |
|
|
235
312
|
| `--no-daemon` | Force direct execution | off |
|
|
236
313
|
| `--graphs-dir PATH` | Graph export directory | `~/.stata-cli/graphs/` |
|
|
314
|
+
| `--graph-format [png\|svg\|pdf]` | Graph export format | `png` |
|
|
315
|
+
| `--log PATH` | Save output to a log file | off |
|
|
237
316
|
|
|
238
317
|
### JSON Output
|
|
239
318
|
|
|
@@ -313,6 +392,21 @@ regress price mpg weight
|
|
|
313
392
|
predict yhat
|
|
314
393
|
list make price yhat in 1/5"
|
|
315
394
|
|
|
395
|
+
# Retrieve regression results as structured JSON
|
|
396
|
+
stata-cli return e
|
|
397
|
+
|
|
398
|
+
# Get coefficient matrix
|
|
399
|
+
stata-cli matrix e(b)
|
|
400
|
+
|
|
401
|
+
# Inspect variable metadata
|
|
402
|
+
stata-cli vars price mpg weight
|
|
403
|
+
|
|
404
|
+
# Check value labels
|
|
405
|
+
stata-cli labels --var foreign
|
|
406
|
+
|
|
407
|
+
# Read system macros
|
|
408
|
+
stata-cli macro get "c(N)"
|
|
409
|
+
|
|
316
410
|
# Check data after loading
|
|
317
411
|
stata-cli data --if "price>10000"
|
|
318
412
|
|
|
@@ -325,6 +419,9 @@ describe"
|
|
|
325
419
|
|
|
326
420
|
# JSON mode for structured parsing
|
|
327
421
|
stata-cli --json run "display 1+1"
|
|
422
|
+
|
|
423
|
+
# Export graph as SVG
|
|
424
|
+
stata-cli --graph-format svg run "scatter price mpg"
|
|
328
425
|
```
|
|
329
426
|
|
|
330
427
|
## Contributing
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# stata-cli
|
|
2
2
|
|
|
3
|
+
> **Stata CLI Is All Reg Monkeys Need**
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
3
7
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](https://www.python.org/)
|
|
5
9
|
[](https://www.npmjs.com/package/stata-cli)
|
|
6
10
|
|
|
7
11
|
[中文版](README.zh.md) | [English](README.md)
|
|
@@ -26,18 +30,25 @@ A command-line interface for [Stata](https://www.stata.com/) via PyStata — bui
|
|
|
26
30
|
| **Run Code** | Execute inline Stata code, multi-line blocks, or pipe from stdin |
|
|
27
31
|
| **Do Files** | Run `.do` files with `///` line continuation support and graph auto-naming |
|
|
28
32
|
| **Data Viewer** | View current dataset as JSON with `if`-condition filtering and row limits |
|
|
33
|
+
| **Variable Metadata** | Inspect variable names, types, formats, and labels via `vars` |
|
|
34
|
+
| **Stored Results** | Retrieve r(), e(), s() results as structured JSON via `return` |
|
|
35
|
+
| **Matrix Access** | Read Stata matrices (e.g. `e(b)`, `e(V)`) as JSON via `matrix` |
|
|
36
|
+
| **Value Labels** | List and inspect value labels via `labels` |
|
|
37
|
+
| **Macro Access** | Get/set Stata macros including `c()`, `e()`, `r()` system macros |
|
|
38
|
+
| **Frame Management** | List Stata frames and current working frame via `frame` |
|
|
29
39
|
| **Help System** | Browse Stata help topics with SMCL-to-plain-text conversion |
|
|
30
|
-
| **Graph Export** | Auto-detect and export graphs as PNG to `~/.stata-cli/graphs/` |
|
|
40
|
+
| **Graph Export** | Auto-detect and export graphs as PNG/SVG/PDF to `~/.stata-cli/graphs/` |
|
|
31
41
|
| **Daemon Mode** | Persistent background process for sub-second execution via Unix socket |
|
|
32
|
-
| **Output Control** | Compact mode, JSON output, token limit management
|
|
42
|
+
| **Output Control** | Compact mode, JSON output, token limit management, log file output |
|
|
33
43
|
| **Interruption** | Send break signal to stop long-running commands |
|
|
44
|
+
| **Skill Library** | Built-in Stata reference with 57 topics: syntax, econometrics, causal inference, packages |
|
|
34
45
|
|
|
35
46
|
## Installation & Quick Start
|
|
36
47
|
|
|
37
48
|
### Requirements
|
|
38
49
|
|
|
39
50
|
- **Stata 17+** installed on your machine (provides the PyStata library)
|
|
40
|
-
- Python 3.
|
|
51
|
+
- Python 3.9+
|
|
41
52
|
|
|
42
53
|
### Quick Start (Human Users)
|
|
43
54
|
|
|
@@ -184,6 +195,72 @@ stata-cli detect
|
|
|
184
195
|
|
|
185
196
|
Prints the auto-detected Stata installation path.
|
|
186
197
|
|
|
198
|
+
### `return` — Retrieve Stored Results
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
stata-cli return r # r() results (after summarize, etc.)
|
|
202
|
+
stata-cli return e # e() results (after regress, etc.)
|
|
203
|
+
stata-cli return s # s() results
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Returns r(), e(), or s() stored results as structured JSON — scalars, macros, and matrix references.
|
|
207
|
+
|
|
208
|
+
### `vars` — Variable Metadata
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
stata-cli vars # all variables
|
|
212
|
+
stata-cli vars price mpg # specific variables
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Returns variable names, types, formats, and labels as JSON. More structured than `describe`.
|
|
216
|
+
|
|
217
|
+
### `matrix` — Read Stata Matrices
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
stata-cli matrix e(b) # coefficient vector
|
|
221
|
+
stata-cli matrix e(V) # variance-covariance matrix
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Returns matrix data, dimensions, and row/column names as JSON.
|
|
225
|
+
|
|
226
|
+
### `labels` — Value Labels
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
stata-cli labels # list all value label names
|
|
230
|
+
stata-cli labels origin # show value-label mapping
|
|
231
|
+
stata-cli labels --var foreign # show label attached to a variable
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### `macro` — Get/Set Macros
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
stata-cli macro get "c(current_date)"
|
|
238
|
+
stata-cli macro get "e(cmd)"
|
|
239
|
+
stata-cli macro set myvar "hello"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Access Stata macros including system macros (`c()`, `e()`, `r()`).
|
|
243
|
+
|
|
244
|
+
### `frame` — List Frames
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
stata-cli frame
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Shows all Stata frames and the current working frame.
|
|
251
|
+
|
|
252
|
+
### `skill` — Stata Reference Library
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
stata-cli skill # overview: gotchas, patterns, topic routing table
|
|
256
|
+
stata-cli skill --list # list all 57 topics with descriptions
|
|
257
|
+
stata-cli skill regression # linear regression reference
|
|
258
|
+
stata-cli skill did # difference-in-differences guide
|
|
259
|
+
stata-cli skill reghdfe # reghdfe package guide
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Built-in reference library covering data management, econometrics, causal inference, graphics, Mata programming, and 20+ community packages. Aliases supported (e.g. `did` for `difference-in-differences`, `panel` for `panel-data`).
|
|
263
|
+
|
|
187
264
|
## Daemon Mode
|
|
188
265
|
|
|
189
266
|
The daemon keeps PyStata alive in the background — reduces execution time from **~2-3s to ~85ms** (35x speedup).
|
|
@@ -221,6 +298,8 @@ The daemon auto-shuts down after 1 hour of inactivity (configurable with `--idle
|
|
|
221
298
|
| `--max-tokens N` | Max output tokens (0=unlimited) | 0 |
|
|
222
299
|
| `--no-daemon` | Force direct execution | off |
|
|
223
300
|
| `--graphs-dir PATH` | Graph export directory | `~/.stata-cli/graphs/` |
|
|
301
|
+
| `--graph-format [png\|svg\|pdf]` | Graph export format | `png` |
|
|
302
|
+
| `--log PATH` | Save output to a log file | off |
|
|
224
303
|
|
|
225
304
|
### JSON Output
|
|
226
305
|
|
|
@@ -300,6 +379,21 @@ regress price mpg weight
|
|
|
300
379
|
predict yhat
|
|
301
380
|
list make price yhat in 1/5"
|
|
302
381
|
|
|
382
|
+
# Retrieve regression results as structured JSON
|
|
383
|
+
stata-cli return e
|
|
384
|
+
|
|
385
|
+
# Get coefficient matrix
|
|
386
|
+
stata-cli matrix e(b)
|
|
387
|
+
|
|
388
|
+
# Inspect variable metadata
|
|
389
|
+
stata-cli vars price mpg weight
|
|
390
|
+
|
|
391
|
+
# Check value labels
|
|
392
|
+
stata-cli labels --var foreign
|
|
393
|
+
|
|
394
|
+
# Read system macros
|
|
395
|
+
stata-cli macro get "c(N)"
|
|
396
|
+
|
|
303
397
|
# Check data after loading
|
|
304
398
|
stata-cli data --if "price>10000"
|
|
305
399
|
|
|
@@ -312,6 +406,9 @@ describe"
|
|
|
312
406
|
|
|
313
407
|
# JSON mode for structured parsing
|
|
314
408
|
stata-cli --json run "display 1+1"
|
|
409
|
+
|
|
410
|
+
# Export graph as SVG
|
|
411
|
+
stata-cli --graph-format svg run "scatter price mpg"
|
|
315
412
|
```
|
|
316
413
|
|
|
317
414
|
## Contributing
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "stata-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Command-line interface for running Stata commands via PyStata"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.9"
|
|
7
7
|
license = {text = "MIT"}
|
|
8
8
|
keywords = ["stata", "cli", "statistics", "data-science"]
|
|
9
9
|
|
|
@@ -23,3 +23,6 @@ build-backend = "setuptools.build_meta"
|
|
|
23
23
|
|
|
24
24
|
[tool.setuptools.packages.find]
|
|
25
25
|
where = ["src"]
|
|
26
|
+
|
|
27
|
+
[tool.setuptools.package-data]
|
|
28
|
+
stata_cli = ["skills/**/*.md"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.0"
|
|
@@ -162,6 +162,37 @@ class DaemonServer:
|
|
|
162
162
|
ok = self._engine.stop()
|
|
163
163
|
_send_msg(conn, {"status": "ok" if ok else "no_op"})
|
|
164
164
|
|
|
165
|
+
elif cmd_type == "get_return":
|
|
166
|
+
data = self._engine.get_return(rtype=payload.get("rtype", "r"))
|
|
167
|
+
_send_msg(conn, data)
|
|
168
|
+
|
|
169
|
+
elif cmd_type == "get_vars":
|
|
170
|
+
data = self._engine.get_vars()
|
|
171
|
+
_send_msg(conn, data)
|
|
172
|
+
|
|
173
|
+
elif cmd_type == "get_matrix":
|
|
174
|
+
data = self._engine.get_matrix(payload.get("name", ""))
|
|
175
|
+
_send_msg(conn, data)
|
|
176
|
+
|
|
177
|
+
elif cmd_type == "get_labels":
|
|
178
|
+
data = self._engine.get_labels(
|
|
179
|
+
name=payload.get("name"),
|
|
180
|
+
var=payload.get("var"),
|
|
181
|
+
)
|
|
182
|
+
_send_msg(conn, data)
|
|
183
|
+
|
|
184
|
+
elif cmd_type == "get_macro":
|
|
185
|
+
data = self._engine.get_macro(payload.get("name", ""))
|
|
186
|
+
_send_msg(conn, data)
|
|
187
|
+
|
|
188
|
+
elif cmd_type == "set_macro":
|
|
189
|
+
data = self._engine.set_macro(payload.get("name", ""), payload.get("value", ""))
|
|
190
|
+
_send_msg(conn, data)
|
|
191
|
+
|
|
192
|
+
elif cmd_type == "get_frames":
|
|
193
|
+
data = self._engine.get_frames()
|
|
194
|
+
_send_msg(conn, data)
|
|
195
|
+
|
|
165
196
|
elif cmd_type == "status":
|
|
166
197
|
_send_msg(conn, {
|
|
167
198
|
"status": "ok",
|
|
@@ -50,14 +50,18 @@ _EXISTING_GRAPHN_RE = re.compile(r"\bname\s*\(\s*graph(\d+)", re.IGNORECASE)
|
|
|
50
50
|
class StataEngine:
|
|
51
51
|
"""Thin wrapper around PyStata for single-process command execution."""
|
|
52
52
|
|
|
53
|
-
def __init__(self, stata_path: str, edition: str = "mp", graphs_dir: Optional[str] = None):
|
|
53
|
+
def __init__(self, stata_path: str, edition: str = "mp", graphs_dir: Optional[str] = None, graph_format: str = "png"):
|
|
54
54
|
self.stata_path = stata_path
|
|
55
55
|
self.edition = edition.lower()
|
|
56
56
|
self.graphs_dir = graphs_dir or get_graphs_root()
|
|
57
|
+
self.graph_format = graph_format.lower()
|
|
57
58
|
self._stata = None
|
|
58
59
|
self._stlib = None
|
|
59
60
|
self._initialized = False
|
|
60
61
|
self._stop_sent = False
|
|
62
|
+
self._last_r: dict = {}
|
|
63
|
+
self._last_e: dict = {}
|
|
64
|
+
self._last_s: dict = {}
|
|
61
65
|
|
|
62
66
|
def _ensure_initialized(self) -> None:
|
|
63
67
|
if self._initialized:
|
|
@@ -89,6 +93,13 @@ class StataEngine:
|
|
|
89
93
|
|
|
90
94
|
self._stata = stata_module
|
|
91
95
|
|
|
96
|
+
if self.graph_format != "png":
|
|
97
|
+
try:
|
|
98
|
+
from pystata import config as pystata_config # type: ignore[import-untyped]
|
|
99
|
+
pystata_config.set_graph_format(self.graph_format)
|
|
100
|
+
except Exception:
|
|
101
|
+
pass
|
|
102
|
+
|
|
92
103
|
try:
|
|
93
104
|
from pystata.config import stlib as stlib_module # type: ignore[import-untyped]
|
|
94
105
|
self._stlib = stlib_module
|
|
@@ -113,19 +124,27 @@ class StataEngine:
|
|
|
113
124
|
)
|
|
114
125
|
log_file_stata = log_file.replace("\\", "/")
|
|
115
126
|
|
|
116
|
-
|
|
127
|
+
setup = (
|
|
117
128
|
f'capture log close _all\n'
|
|
118
129
|
f'log using "{log_file_stata}", replace text\n'
|
|
119
|
-
f'{code}\n'
|
|
120
|
-
f'capture log close _all\n'
|
|
121
130
|
)
|
|
131
|
+
teardown = 'capture log close _all\n'
|
|
122
132
|
|
|
123
133
|
start = time.time()
|
|
124
134
|
try:
|
|
125
135
|
old_stdout = sys.stdout
|
|
126
136
|
sys.stdout = io.StringIO()
|
|
127
137
|
try:
|
|
128
|
-
self._stata.run(
|
|
138
|
+
self._stata.run(setup + code, echo=True, inline=False)
|
|
139
|
+
finally:
|
|
140
|
+
sys.stdout = old_stdout
|
|
141
|
+
|
|
142
|
+
self._capture_stored_results()
|
|
143
|
+
|
|
144
|
+
old_stdout = sys.stdout
|
|
145
|
+
sys.stdout = io.StringIO()
|
|
146
|
+
try:
|
|
147
|
+
self._stata.run(teardown, echo=False, inline=False)
|
|
129
148
|
finally:
|
|
130
149
|
captured_stdout = sys.stdout.getvalue()
|
|
131
150
|
sys.stdout = old_stdout
|
|
@@ -300,6 +319,119 @@ class StataEngine:
|
|
|
300
319
|
|
|
301
320
|
return result
|
|
302
321
|
|
|
322
|
+
def get_return(self, rtype: str = "r") -> Dict[str, Any]:
|
|
323
|
+
"""Retrieve stored results: r(), e(), or s()."""
|
|
324
|
+
self._ensure_initialized()
|
|
325
|
+
try:
|
|
326
|
+
if rtype == "r":
|
|
327
|
+
return {"status": "success", "type": "r", "results": dict(self._last_r)}
|
|
328
|
+
elif rtype == "e":
|
|
329
|
+
return {"status": "success", "type": "e", "results": dict(self._last_e)}
|
|
330
|
+
elif rtype == "s":
|
|
331
|
+
return {"status": "success", "type": "s", "results": dict(self._last_s)}
|
|
332
|
+
else:
|
|
333
|
+
return {"status": "error", "error": f"Unknown return type: {rtype}"}
|
|
334
|
+
except Exception as exc:
|
|
335
|
+
return {"status": "error", "error": str(exc)}
|
|
336
|
+
|
|
337
|
+
def get_vars(self) -> Dict[str, Any]:
|
|
338
|
+
"""Return variable metadata for the current dataset."""
|
|
339
|
+
self._ensure_initialized()
|
|
340
|
+
try:
|
|
341
|
+
import sfi # type: ignore[import-untyped]
|
|
342
|
+
nvar = sfi.Data.getVarCount()
|
|
343
|
+
nobs = sfi.Data.getObsTotal()
|
|
344
|
+
variables = []
|
|
345
|
+
for i in range(nvar):
|
|
346
|
+
name = sfi.Data.getVarName(i)
|
|
347
|
+
variables.append({
|
|
348
|
+
"name": name,
|
|
349
|
+
"type": sfi.Data.getVarType(i),
|
|
350
|
+
"format": sfi.Data.getVarFormat(i),
|
|
351
|
+
"label": sfi.Data.getVarLabel(i),
|
|
352
|
+
"is_string": sfi.Data.isVarTypeStr(i),
|
|
353
|
+
})
|
|
354
|
+
return {
|
|
355
|
+
"status": "success",
|
|
356
|
+
"n_vars": nvar,
|
|
357
|
+
"n_obs": nobs,
|
|
358
|
+
"variables": variables,
|
|
359
|
+
}
|
|
360
|
+
except Exception as exc:
|
|
361
|
+
return {"status": "error", "error": str(exc)}
|
|
362
|
+
|
|
363
|
+
def get_matrix(self, name: str) -> Dict[str, Any]:
|
|
364
|
+
"""Return a Stata matrix as a dict."""
|
|
365
|
+
self._ensure_initialized()
|
|
366
|
+
try:
|
|
367
|
+
import sfi # type: ignore[import-untyped]
|
|
368
|
+
nrows = sfi.Matrix.getRowTotal(name)
|
|
369
|
+
ncols = sfi.Matrix.getColTotal(name)
|
|
370
|
+
row_names = sfi.Matrix.getRowNames(name)
|
|
371
|
+
col_names = sfi.Matrix.getColNames(name)
|
|
372
|
+
data = sfi.Matrix.get(name)
|
|
373
|
+
return {
|
|
374
|
+
"status": "success",
|
|
375
|
+
"name": name,
|
|
376
|
+
"rows": nrows,
|
|
377
|
+
"cols": ncols,
|
|
378
|
+
"row_names": row_names,
|
|
379
|
+
"col_names": col_names,
|
|
380
|
+
"data": data,
|
|
381
|
+
}
|
|
382
|
+
except Exception as exc:
|
|
383
|
+
return {"status": "error", "error": str(exc)}
|
|
384
|
+
|
|
385
|
+
def get_labels(self, name: Optional[str] = None, var: Optional[str] = None) -> Dict[str, Any]:
|
|
386
|
+
"""Return value labels."""
|
|
387
|
+
self._ensure_initialized()
|
|
388
|
+
try:
|
|
389
|
+
import sfi # type: ignore[import-untyped]
|
|
390
|
+
if var:
|
|
391
|
+
label_name = sfi.ValueLabel.getVarValueLabel(var)
|
|
392
|
+
if not label_name:
|
|
393
|
+
return {"status": "success", "variable": var, "label_name": "", "labels": {}}
|
|
394
|
+
mapping = sfi.ValueLabel.getValueLabels(label_name)
|
|
395
|
+
return {"status": "success", "variable": var, "label_name": label_name, "labels": mapping}
|
|
396
|
+
if name:
|
|
397
|
+
mapping = sfi.ValueLabel.getValueLabels(name)
|
|
398
|
+
return {"status": "success", "name": name, "labels": mapping}
|
|
399
|
+
names = sfi.ValueLabel.getNames()
|
|
400
|
+
return {"status": "success", "names": names}
|
|
401
|
+
except Exception as exc:
|
|
402
|
+
return {"status": "error", "error": str(exc)}
|
|
403
|
+
|
|
404
|
+
def get_macro(self, name: str) -> Dict[str, Any]:
|
|
405
|
+
"""Get the value of a Stata macro."""
|
|
406
|
+
self._ensure_initialized()
|
|
407
|
+
try:
|
|
408
|
+
import sfi # type: ignore[import-untyped]
|
|
409
|
+
value = sfi.Macro.getGlobal(name)
|
|
410
|
+
return {"status": "success", "name": name, "value": value}
|
|
411
|
+
except Exception as exc:
|
|
412
|
+
return {"status": "error", "error": str(exc)}
|
|
413
|
+
|
|
414
|
+
def set_macro(self, name: str, value: str) -> Dict[str, Any]:
|
|
415
|
+
"""Set a Stata global macro."""
|
|
416
|
+
self._ensure_initialized()
|
|
417
|
+
try:
|
|
418
|
+
import sfi # type: ignore[import-untyped]
|
|
419
|
+
sfi.Macro.setGlobal(name, value)
|
|
420
|
+
return {"status": "success", "name": name, "value": value}
|
|
421
|
+
except Exception as exc:
|
|
422
|
+
return {"status": "error", "error": str(exc)}
|
|
423
|
+
|
|
424
|
+
def get_frames(self) -> Dict[str, Any]:
|
|
425
|
+
"""Return list of Stata frames and the current working frame."""
|
|
426
|
+
self._ensure_initialized()
|
|
427
|
+
try:
|
|
428
|
+
import sfi # type: ignore[import-untyped]
|
|
429
|
+
frames = sfi.Frame.getFrames()
|
|
430
|
+
cwf = sfi.Frame.getCWF()
|
|
431
|
+
return {"status": "success", "frames": frames, "current": cwf}
|
|
432
|
+
except Exception as exc:
|
|
433
|
+
return {"status": "error", "error": str(exc)}
|
|
434
|
+
|
|
303
435
|
def stop(self) -> bool:
|
|
304
436
|
"""Interrupt a running Stata command. Returns True if signal sent."""
|
|
305
437
|
if self._stop_sent or self._stlib is None:
|
|
@@ -316,6 +448,44 @@ class StataEngine:
|
|
|
316
448
|
|
|
317
449
|
# ── graph detection ──────────────────────────────────────────────────
|
|
318
450
|
|
|
451
|
+
def _capture_stored_results(self) -> None:
|
|
452
|
+
"""Snapshot r(), e(), s() results via sfi before log close clears them."""
|
|
453
|
+
try:
|
|
454
|
+
import sfi # type: ignore[import-untyped]
|
|
455
|
+
except ImportError:
|
|
456
|
+
return
|
|
457
|
+
|
|
458
|
+
for rtype, store in [("r", "_last_r"), ("e", "_last_e"), ("s", "_last_s")]:
|
|
459
|
+
result: Dict[str, Any] = {}
|
|
460
|
+
cat = f"{rtype}()"
|
|
461
|
+
try:
|
|
462
|
+
scalar_names = sfi.SFIToolkit.listReturn(cat, "scalar")
|
|
463
|
+
if scalar_names and scalar_names.strip():
|
|
464
|
+
for name in scalar_names.strip().split():
|
|
465
|
+
try:
|
|
466
|
+
result[name] = sfi.Scalar.getValue(f"{rtype}({name})")
|
|
467
|
+
except Exception:
|
|
468
|
+
pass
|
|
469
|
+
|
|
470
|
+
macro_names = sfi.SFIToolkit.listReturn(cat, "macro")
|
|
471
|
+
if macro_names and macro_names.strip():
|
|
472
|
+
for name in macro_names.strip().split():
|
|
473
|
+
try:
|
|
474
|
+
result[name] = sfi.Macro.getGlobal(f"{rtype}({name})")
|
|
475
|
+
except Exception:
|
|
476
|
+
pass
|
|
477
|
+
|
|
478
|
+
if rtype != "s":
|
|
479
|
+
matrix_names = sfi.SFIToolkit.listReturn(cat, "matrix")
|
|
480
|
+
if matrix_names and matrix_names.strip():
|
|
481
|
+
for name in matrix_names.strip().split():
|
|
482
|
+
result[f"matrix:{name}"] = f"[matrix, use 'stata-cli matrix {rtype}({name})']"
|
|
483
|
+
except Exception:
|
|
484
|
+
pass
|
|
485
|
+
setattr(self, store, result)
|
|
486
|
+
|
|
487
|
+
# ── graph detection (continued) ─────────────────────────────────────
|
|
488
|
+
|
|
319
489
|
def _reset_graph_tracking(self) -> None:
|
|
320
490
|
if self._stlib is None:
|
|
321
491
|
return
|
|
@@ -348,12 +518,15 @@ class StataEngine:
|
|
|
348
518
|
self._stlib.StataSO_Execute(
|
|
349
519
|
get_encode_str(f"quietly graph display {gname}"), False
|
|
350
520
|
)
|
|
351
|
-
|
|
521
|
+
ext = self.graph_format
|
|
522
|
+
graph_file = os.path.join(batch["batch_dir"], f"{gname}.{ext}")
|
|
352
523
|
graph_file_stata = graph_file.replace("\\", "/")
|
|
524
|
+
fmt_opt = f"as({ext}) " if ext != "png" else ""
|
|
525
|
+
size_opt = "width(800) height(600)" if ext == "png" else ""
|
|
353
526
|
export_cmd = (
|
|
354
527
|
f'quietly graph export "{graph_file_stata}", '
|
|
355
|
-
f"name({gname}) replace
|
|
356
|
-
)
|
|
528
|
+
f"{fmt_opt}name({gname}) replace {size_opt}"
|
|
529
|
+
).rstrip()
|
|
357
530
|
rc = self._stlib.StataSO_Execute(get_encode_str(export_cmd), False)
|
|
358
531
|
if rc != 0:
|
|
359
532
|
continue
|