Script Translator
Faithful line-by-line translation between Stata, Python, and R.
1. Detect Source and Target Languages
From the user message: Look for keywords like "to Python", "in R", "port to Stata", "convert to R", "rewrite in Python".
From file extension:
.do → Stata
.py → Python
.R / .r → R
If ambiguous: Ask the user to confirm source and target before proceeding.
2. Load the Language-Pair Body File
Before writing ANY translated code, read the relevant body file:
| Direction |
File to read |
| Python → R |
bodies/python-to-r.md |
| Python → Stata |
bodies/python-to-stata.md |
| R → Python |
bodies/r-to-python.md |
| R → Stata |
bodies/r-to-stata.md |
| Stata → Python |
bodies/stata-to-python.md |
| Stata → R |
bodies/stata-to-r.md |
The body file contains the construct mapping table, idiomatic equivalences, required imports, and known traps for that specific pair. Follow it closely.
3. Core Translation Rules (All Language Pairs)
These rules are absolute and override any instinct to "improve" the code:
3a. Structure and Logic
- Translate line by line, preserving the order and structure of the original
- Keep variable names identical — never rename columns, variables, or macros
- Preserve all data filters and conditions exactly — same logic, same thresholds
- DO NOT refactor, optimize, or restructure the code
- DO NOT change the order of operations unless strictly required by the target language
- If the original has a bug, translate the bug faithfully and add
# REVIEW: possible bug in original
- Add short inline comments when a translated line is non-obvious (e.g.,
# Stata's egen mean() equivalent)
- Flag with
# REVIEW: any construct that has no direct equivalent or requires manual verification
3b. Methodology (Non-Negotiable)
The translated script must produce statistically identical results:
- Same estimator — OLS stays OLS, logit stays logit, probit stays probit. Never substitute.
- Same standard errors —
robust (HC1) stays HC1. cluster(var) stays clustered at the same variable. Never change the SE type or clustering level. Verify the target language's default matches (e.g., Stata robust = HC1, statsmodels HC1 = HC1, but statsmodels default is not robust).
- Same fixed effects —
areg, absorb(fe) → must absorb the same variable, not include as dummies unless no absorb equivalent exists. Flag with # REVIEW: if the approach differs.
- Same merge logic — left join stays left join, m:1 stays m:1. Preserve
validate checks. Add _merge diagnostics if the original inspects them.
- Same sample restrictions — every
keep if, drop if, filter, and subsetting condition must be translated exactly. Missing value handling differs across languages — explicitly handle this (see body files for language-specific traps).
- Same weighting —
[pw=wt], [aw=wt], [fw=wt] must map to the exact equivalent. Flag if the target language doesn't distinguish weight types.
- Same test statistics — F-tests, t-tests, Wald tests, correlation tests must use the same test.
pwcorr, sig → Pearson with p-values, not Spearman.
- Same confidence level — if the original uses 95% CI, the translation uses 95% CI. Same for significance stars (* 0.10 ** 0.05 *** 0.01).
3c. Visual Fidelity (Non-Negotiable)
The translated figure must be visually identical to the original. Go through every visual property line by line:
- Same plot type — scatter stays scatter, bar stays bar, line stays line
- Same colors —
blue stays blue, red stays red. Match the exact color name or hex code.
- Same transparency/alpha —
alpha=0.7 → %70 in Stata, alpha = 0.7 in R. Never omit.
- Same marker properties — marker shape, size, edge color, edge width. If the original has
edgecolors="white", linewidth=0.5, the translation must replicate this (Stata: mlcolor(white) mlwidth(vthin)).
- Same grid — if the original has
ax.grid(True, alpha=0.3), the translation must include grid lines. If there is no grid, the translation must not add one.
- Same axis labels, title, and legend — identical text, same position, same ordering
- Same legend position —
position(5) ring(0) = lower right inside. Default upper-right → translate to upper-right. Never leave to default if the original specifies a position.
- Same point labels/annotations — if points are labeled with state names, font size, position offset, and alignment must all be replicated as closely as the target language allows.
- Same axis range and ticks — if the original sets
ylim, xlim, or custom tick marks, replicate them. If it uses auto-scaling, let the target auto-scale too.
- Same figure size —
figsize=(8, 6) → target equivalent (Stata: xsize(8) ysize(6); R: width = 8, height = 6)
- Same background — white background must be explicitly set in every language (it is NOT the default in Stata or base R)
- Same output format and resolution — PNG at 300 DPI. Always.
If a visual property cannot be exactly replicated, add a # REVIEW: [property] has no exact equivalent in [target language] comment explaining the closest approximation used.
4. Apply Project Boilerplate
After translation, wrap the script in the project's conventions:
Python target:
import config as cfg for paths
cfg.FIGURES / "name.png" for figure output
dpi=300, bbox_inches='tight', plt.close(fig)
- Module docstring with inputs/outputs
Stata target:
version 17, clear all, set more off
do "$root/code/stata/config_local.do" for $data_root
log using "$root/quality_reports/stata_logs/[name].log", replace
graphregion(color(white)) bgcolor(white), width(2400)
log close at end
R target:
library() calls at top
here::here() for paths
ggsave(width = 8, height = 6, dpi = 300)
set.seed(42) if stochastic
5. Output Format
Present the translation as:
- Translated script in a fenced code block
- Translation notes section listing:
- Any
# REVIEW: items and why they were flagged
- Differences in default behavior between languages (e.g., 0-vs-1 indexing)
- Packages/libraries the target script requires
6. Run and Verify
After writing the translated script, run it and check for errors:
- Python:
python code/py/<script>.py
- Stata:
stata -b do code/stata/<script>.do (ALWAYS use the stata wrapper, never call StataSE-64.exe directly — the wrapper auto-moves batch logs to quality_reports/stata_logs/). Then check log for r(
- R:
Rscript code/R/<script>.R
If it fails, fix only the syntax error (not the logic) and re-run. If the failure is due to a bug in the original, note it in Translation Notes.
1---2name: script-translator3description: Use this skill whenever the user wants to translate, convert, or port a script from one statistical programming language to another — STATA, Python (pandas/numpy/statsmodels), or R (tidyverse/base R). Trigger even if the user says 'rewrite in R', 'convert to Python', 'port to STATA', or asks how code would look in another language.4---5
6# Script Translator
7
8Faithful line-by-line translation between Stata, Python, and R.
9
10## 1. Detect Source and Target Languages
11
12**From the user message:** Look for keywords like "to Python", "in R", "port to Stata", "convert to R", "rewrite in Python".
13
14**From file extension:**
15- `.do` → Stata
16- `.py` → Python
17- `.R` / `.r` → R
18
19**If ambiguous:** Ask the user to confirm source and target before proceeding.
20
21## 2. Load the Language-Pair Body File
22
23Before writing ANY translated code, read the relevant body file:
24
25| Direction | File to read |
26|-----------|-------------|
27| Python → R | `bodies/python-to-r.md` |
28| Python → Stata | `bodies/python-to-stata.md` |
29| R → Python | `bodies/r-to-python.md` |
30| R → Stata | `bodies/r-to-stata.md` |
31| Stata → Python | `bodies/stata-to-python.md` |
32| Stata → R | `bodies/stata-to-r.md` |
33
34The body file contains the construct mapping table, idiomatic equivalences, required imports, and known traps for that specific pair. Follow it closely.
35
36## 3. Core Translation Rules (All Language Pairs)
37
38These rules are absolute and override any instinct to "improve" the code:
39
40### 3a. Structure and Logic
411. **Translate line by line**, preserving the order and structure of the original
422. **Keep variable names identical** — never rename columns, variables, or macros
433. **Preserve all data filters and conditions exactly** — same logic, same thresholds
444. **DO NOT refactor, optimize, or restructure** the code
455. **DO NOT change the order of operations** unless strictly required by the target language
466. **If the original has a bug, translate the bug faithfully** and add `# REVIEW: possible bug in original`
477. **Add short inline comments** when a translated line is non-obvious (e.g., `# Stata's egen mean() equivalent`)
488. **Flag with `# REVIEW:`** any construct that has no direct equivalent or requires manual verification
49
50### 3b. Methodology (Non-Negotiable)
51The translated script must produce **statistically identical results**:
52
539. **Same estimator** — OLS stays OLS, logit stays logit, probit stays probit. Never substitute.
5410. **Same standard errors** — `robust` (HC1) stays HC1. `cluster(var)` stays clustered at the same variable. Never change the SE type or clustering level. Verify the target language's default matches (e.g., Stata `robust` = HC1, statsmodels `HC1` = HC1, but statsmodels default is not robust).
5511. **Same fixed effects** — `areg, absorb(fe)` → must absorb the same variable, not include as dummies unless no absorb equivalent exists. Flag with `# REVIEW:` if the approach differs.
5612. **Same merge logic** — left join stays left join, m:1 stays m:1. Preserve `validate` checks. Add `_merge` diagnostics if the original inspects them.
5713. **Same sample restrictions** — every `keep if`, `drop if`, filter, and subsetting condition must be translated exactly. Missing value handling differs across languages — explicitly handle this (see body files for language-specific traps).
5814. **Same weighting** — `[pw=wt]`, `[aw=wt]`, `[fw=wt]` must map to the exact equivalent. Flag if the target language doesn't distinguish weight types.
5915. **Same test statistics** — F-tests, t-tests, Wald tests, correlation tests must use the same test. `pwcorr, sig` → Pearson with p-values, not Spearman.
6016. **Same confidence level** — if the original uses 95% CI, the translation uses 95% CI. Same for significance stars (* 0.10 ** 0.05 *** 0.01).
61
62### 3c. Visual Fidelity (Non-Negotiable)
63The translated figure must be **visually identical** to the original. Go through every visual property line by line:
64
6517. **Same plot type** — scatter stays scatter, bar stays bar, line stays line
6618. **Same colors** — `blue` stays `blue`, `red` stays `red`. Match the exact color name or hex code.
6719. **Same transparency/alpha** — `alpha=0.7` → `%70` in Stata, `alpha = 0.7` in R. Never omit.
6820. **Same marker properties** — marker shape, size, edge color, edge width. If the original has `edgecolors="white", linewidth=0.5`, the translation must replicate this (Stata: `mlcolor(white) mlwidth(vthin)`).
6921. **Same grid** — if the original has `ax.grid(True, alpha=0.3)`, the translation must include grid lines. If there is no grid, the translation must not add one.
7022. **Same axis labels, title, and legend** — identical text, same position, same ordering
7123. **Same legend position** — `position(5) ring(0)` = lower right inside. Default upper-right → translate to upper-right. Never leave to default if the original specifies a position.
7224. **Same point labels/annotations** — if points are labeled with state names, font size, position offset, and alignment must all be replicated as closely as the target language allows.
7325. **Same axis range and ticks** — if the original sets `ylim`, `xlim`, or custom tick marks, replicate them. If it uses auto-scaling, let the target auto-scale too.
7426. **Same figure size** — `figsize=(8, 6)` → target equivalent (Stata: `xsize(8) ysize(6)`; R: `width = 8, height = 6`)
7527. **Same background** — white background must be explicitly set in every language (it is NOT the default in Stata or base R)
7628. **Same output format and resolution** — PNG at 300 DPI. Always.
77
78**If a visual property cannot be exactly replicated**, add a `# REVIEW: [property] has no exact equivalent in [target language]` comment explaining the closest approximation used.
79
80## 4. Apply Project Boilerplate
81
82After translation, wrap the script in the project's conventions:
83
84**Python target:**
85- `import config as cfg` for paths
86- `cfg.FIGURES / "name.png"` for figure output
87- `dpi=300`, `bbox_inches='tight'`, `plt.close(fig)`
88- Module docstring with inputs/outputs
89
90**Stata target:**
91- `version 17`, `clear all`, `set more off`
92- `do "$root/code/stata/config_local.do"` for `$data_root`
93- `log using "$root/quality_reports/stata_logs/[name].log", replace`
94- `graphregion(color(white)) bgcolor(white)`, `width(2400)`
95- `log close` at end
96
97**R target:**
98- `library()` calls at top
99- `here::here()` for paths
100- `ggsave(width = 8, height = 6, dpi = 300)`
101- `set.seed(42)` if stochastic
102
103## 5. Output Format
104
105Present the translation as:
106
1071. **Translated script** in a fenced code block
1082. **Translation notes** section listing:
109 - Any `# REVIEW:` items and why they were flagged
110 - Differences in default behavior between languages (e.g., 0-vs-1 indexing)
111 - Packages/libraries the target script requires
112
113## 6. Run and Verify
114
115After writing the translated script, run it and check for errors:
116
117- **Python:** `python code/py/<script>.py`
118- **Stata:** `stata -b do code/stata/<script>.do` (ALWAYS use the `stata` wrapper, never call `StataSE-64.exe` directly — the wrapper auto-moves batch logs to `quality_reports/stata_logs/`). Then check log for `r(`
119- **R:** `Rscript code/R/<script>.R`
120
121If it fails, fix only the syntax error (not the logic) and re-run. If the failure is due to a bug in the original, note it in Translation Notes.