Skip to content

About

Why not evolve into a monkey? Regression_Monkey runs every regression your referee might ask for — all 2^K control subsets, N-way fixed effects, CGM clustered standard errors — and ships a publication-ready specification curve before the first revision request arrives.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

regression_monkey

Author: zhao_xun@sjtu.edu.cn · License: MIT

中文文档

Dedicated to Haoping Yu, the one I love most.

A standalone specification curve analysis tool for econometric robustness checks. It enumerates all valid combinations of control variables, estimates OLS with N-way absorbed fixed effects, computes heteroskedasticity-robust, one-way clustered, or CGM two-way clustered standard errors, and exports publication-ready figures and a significance summary table.

The workflow and output design draw on Stata's spec_curve command and extend it with batch configuration, parallel execution, and two export formats.

Output modes

PNG mode — static publication-quality figure exported as a raster image.

HTML mode — a self-contained interactive webpage with two view modes. It opens in compact mode by default only when there are more than 1024 specifications; smaller charts disable compact and open in detail. Switch Y, X, and fixed-effect spec from a top selector bar when multiple combinations are present, and sort specifications by coefficient, observation count, or signed significance.

detail is the full interactive view. Hover highlights a specification across all panels, click pins the current specification, and the right details panel lists included controls in input order with per-control statistics. COEF confidence bands are drawn as per-specification slices for a finer PNG-like edge texture; STARS uses COEF-style round points stacked from bottom to top.

When switching Y, X, or fixed-effect spec in the top selector bar, the other selectors keep their current value when that value is still available; fixed-effect specs are preserved by matching the displayed spec label across Y/X combinations. In-chart SORT, MODE, and Controls color selections also persist across top-selector switches when the target chart supports the selected mode, without flashing the default toolbar state during iframe reload.

HTML DETAIL mode example

compact is the dense overview view for large specification sets. It hides the right details panel, disables central-chart hover/click selection, caps the column width at the 8192-specification baseline, and allows horizontal scrolling when the compact plot is wider than the viewport. After rendering, the compact chart is cached as one bitmap and scrolling redraws only the visible slice; sorting, filter chips, guide chips, CI chips, and viewport resizing rebuild that cache. STARS uses segmented CONTROL-like color bars whose heights encode significance level, CONTROL and OBS use thin square bars, and compact COEF points scale to the compact baseline column width.

HTML COMPACT mode example

Both PNG and HTML title metadata show the analysis engine (stata or r) used for the exported figure.

For external engines, elapsed-time metadata includes the engine handoff/setup step that belongs to the run, the model-estimation time, and the export rendering time reported in progress output.

Interactive HTML uses the persisted coefficient-axis scale when re-sorting specifications, so CI bands keep the same visual length implied by the saved ci90/ci95/ci99 values.

The HTML significance legend uses the same colors as the coefficient points; n.s. is black.

HTML TEST GUIDES include all, no, and best. In DETAIL, guide lines cannot be hidden; clicking a guide chip jumps to the next matching specification and cycles when multiple matches exist. In COMPACT, guide chips keep the old show/hide behavior. best is a yellow helper line recomputed for the current filtered view: when the currently visible 3-star significant specifications contain no full-test specification, it marks the visible 3-star specification with the largest number of included controls_test variables, breaking ties by smaller p-value and then original order. The HTML caches these dynamic best lookups in page memory by filter state; the cache is not persisted and is cleared when the page closes.

In DETAIL, the right-side control-coefficient badge text is white for significant controls except +1 and -1, which use black text. Significant badge backgrounds use the same 1/2/3 red-blue color levels as STARS. 0+ and 0- use a light gray background with deep red/deep blue text.

The right-side detail panel always shows content in DETAIL mode, even before any specification is hovered or pinned. Its width is computed from the configured control-variable names and updates with the viewport so variable names display fully without hover tooltips. It always shows all controls_test variables in the original input order. Variables not included in the current specification keep their name and a blank badge box, while coefficient and p-value cells stay empty.

The right-side detail panel header includes a COPY button between the specification index and significance badge. It copies the current specification's included control variable names, space-separated, in the same order as the sidebar list.

In the right-side detail panel, clicking a control-variable row cycles its filter through significant included controls, non-significant included controls, not-controlled specifications, and off. Multiple selected rows are combined with AND semantics, so the main chart removes non-matching specifications and re-packs the remaining columns so the kept specifications are contiguous. Filter state is shown on the control rows themselves, with a small legend above the control coefficients for sig, no-sig, and no-control; CLEAR uses the same button style as COPY in the panel header.

The HTML Controls color switch defaults to gray, continuous gray run-length shading for the control matrix. gray draws only the regular varying matrix controls and does not reserve blank rows for controls_must. Switching to sig colors each included control cell by that control's own coefficient significance, using the same red/blue signed levels as STARS and the right sidebar, with a darker neutral gray for insignificant or missing stats. In sig, all controls_must variables are also drawn in the control matrix, ordered before the regular varying matrix controls, and their row labels show a small leading dot to distinguish them from test controls.

Requirements

Dependencies are declared in pyproject.toml and managed by uv.

uv sync

Core dependencies: numpy, pandas, matplotlib, pyreadstat.

Quick start

The recommended workflow is config-file driven. Copy config/config.example.toml, fill in your variable names and file path, then run:

uv run regression-monkey

Or point to the config explicitly:

uv run regression-monkey config/config.toml

CLI flags override any TOML value:

uv run regression-monkey config/config.toml --dpi 600 --n-jobs 0

All regression commands accept the optional --keepsingletons modifier after their ordinary arguments. Without it, fixed-effect singletons are recursively removed as in Stata reghdfe by default. With it, singleton observations are retained, matching Stata reghdfe, keepsingletons.

Interactive specification searches require the same standardized field input style as --baseline and --bdiff. --drop [MAX] tests removals from the variables listed in controls. Multiple variables listed in x are jointly included in every regression; the first x is the tested coefficient used for ranking and reporting, while the remaining x variables stay in every model. --replace runs the baseline and one regression for each candidate replacing the current control. Repeated replacement candidates are reported and automatically deduplicated.

regmonkey --spec --drop
regmonkey --spec --replace
regmonkey --spec --drop --template
path    : "panel.dta"
y       : outcome
x       : treatment1 treatment2
controls: size leverage current
absorb  : firm year
cluster : firm
replace : current alternative1 alternative2

The final replace: field is required only for --replace. Stata use and global input is rejected; use -t or --template to print the standardized input form before the paste prompt. Standardized input may include extra fields unused by the current command; the parser ignores those fields and their continuation lines so one input can be reused across --baseline, --bdiff, --spec, and --sum.

Use regmonkey --baseline --kjyj to estimate and directly export a KJYJ baseline-regression table. Paste the six standardized fields and finish with a blank line. By default, only the model containing all controls is estimated and exported as column (1), without an intermediate 有控制 header. Add --noncontrols to use the two-column layout: column (1) without controls and column (2) with controls. Every variable under x is jointly included in each requested model. The default table has exactly two equal-width cells per row: the variable cell and the model cell. The first column is centered in every baseline layout, matching the existing centered first column in bdiff tables. Both models use the same absorbed fixed effects and clustering settings. Add --short to keep all controls in estimation while hiding their individual RTF rows and adding a Controls summary row. The command defaults to 8 workers and writes ~/Downloads/<timestamp>_<data-stem>_baseline.rtf.

regmonkey --baseline --kjyj
regmonkey --baseline --kjyj --noncontrols
regmonkey --baseline --kjyj --short
regmonkey --baseline --kjyj --template
path    : "/path/to/panel.dta"
y       : AnalystError
x       : Charts Diagrams Pictures
controls: Lev Size ROA SOE Big4 Growth Loss Top1 Mhold Dual Focus BussComplexity TextReadability TextTone TextWord AnalystHorizon AnalystPortfolio AnalystFirmExp BrokerSize AnalystIndExp AnalystIndSpec
absorb  : code analyst#year
cluster : code analyst

The default RTF shows only (1) and omits the intermediate 有控制 header; --noncontrols restores the 无控制 / 有控制 headings and (1)/(2) numbering. Its default title is the left-aligned, regular-weight 表3 基准回归结果; the digit 3 uses Times New Roman while the Chinese title text uses SimHei, with exactly 26 ASCII spaces between 表3 and 基准回归结果. It reports coefficients and t statistics in full-width Chinese parentheses for every x; unless --short is used, column (2) also lists every control coefficient and t statistic while the matching column (1) cells remain blank. Each requested model reports Constant after the controls. Fixed effects use bdiff-style English labels such as Firm FE and Analyst×Year FE. Constant and fixed-effect names are upright; N and the R in R² are italic, while the Adj. prefix and superscript ² remain upright. The note renders its t symbol in italic. The table reports adjusted R-squared but uses title-case Yes fixed-effect indicators. The Controls row appears only with --short; no RTF mode reports an F statistic. These KJYJ conventions apply only to the RTF. Terminal output stays in a fixed bdiff-style form: by default it prints every x under Controls; with --noncontrols, it first prints every x under No controls, inserts a blank line, and then prints the controlled results. N, adjusted R-squared, and F appear once in each group heading; each x row starts with a [level+direction] code, followed by the variable name, coefficient, p-value, and # marks. Level 3/2/1 badges use red/green/blue; level 0 badges and variable names keep the default terminal color. The b field uses red for positive coefficients and blue for negative coefficients; p-values keep the default terminal color. All KJYJ A4 exports use exact 2.54 cm top/bottom margins and 3.175 cm left/right margins. KJYJ --baseline, --bdiff, --sum, and --def tables all use a total table width of 14.9 cm; each exporter distributes its columns within that fixed width. The KJYJ --sum title has its own fixed placement rule: the paragraph remains left-aligned with the existing 420-twip first-line indent, and exactly 23 ASCII spaces separate 表2 from 主要变量描述性统计. It must not be replaced by RTF automatic centering.

Use --ai [DEF_RTF] with any of those four commands, or with regression-monkey-tables, to place one schema-validated Chinese explanation before the table title. Bare --ai preserves the original behavior. If the optional path points to an RTF generated by regmonkey --def, regmonkey reverse-reads its variable categories, names, symbols, and definitions and adds that structured context to the system prompt for matching variables. For example:

regmonkey --baseline --ai ~/Downloads/20260810_variable_definitions.rtf
regmonkey --bdiff 1000 --ai ~/Downloads/20260810_variable_definitions.rtf

The exporter sends the exact structured table result as JSON to the local /Users/zhaoxun/scripts/test/2batch checkout. 2batch uses deepseek-v4-pro with temperature 1 through the configured stream API, validates the JSON response, and retries an invalid response up to twice with structured validation errors. Temporary Parquet, prompt, schema, TOML, response, and cost files are removed after the call, including on failure; only the final RTF remains. The paragraph uses 10.5-pt SimSun, exact 12-pt line spacing, and a two-character first-line indent; Latin letters and numbers use Times New Roman, and literal variable names are italic. --bdiff --export false --ai makes no LLM call because no table is written.

To inspect whole-sample missing rates, run regmonkey --clean, paste Stata use, global y, global x, and global controls lines, then press one blank line. The report preserves every observation and shows each variable's overall and by-year missing percentage. A separate complete-sample section reports the annual share for which all pasted Y, X, and control variables are non-missing. When global option_reghdfe is present, variables from absorb(...) and vce(cluster ...) get their own missing-rate sections and are also required by the complete-sample definition. year is the default time variable; override it with --time fiscal_year or a pasted global time fiscal_year line.

regmonkey --clean

For an interactive bdiff run, the repetition count defaults to 1,000 and can be overridden positionally. Paste the standardized field settings, then press one blank line after the final field. Stata commands such as use, global, and local are not accepted. Use -t or --template to print the input template before the prompt:

regmonkey --bdiff
regmonkey --bdiff --template

# Match Stata bdiff, bsample: bootstrap the pool, then randomly split it
regmonkey --bdiff 1000 --bootstrap

# Retry seed-42 results with p in [0.05, 0.10] using seeds 0 through 9
regmonkey --bdiff 1000 --seeds

# Retry every finite seed-42 result with p >= 0.05
regmonkey --bdiff 1000 --seeds force

# Merge all otherwise selected group tables horizontally into one RTF
regmonkey --bdiff 1000 --merge --export all

# Put all selected tables in one RTF, starting each subsequent table on a new page
regmonkey --bdiff 1000 --join --export all

# First merge each pipe array horizontally, then join those tables by page
regmonkey --bdiff 1000 --merge --join --export all

# Hide individual controls in RTF tables while retaining Constant
regmonkey --bdiff 1000 --short
path    : ""
y       :
x       :
controls:
absorb  : code analyst#year
cluster : code

groups  :
byvars  : ind#year year all
ties    : low high average(code) average(seed)

All nine fields must be present, but controls is the only field that may be empty. y accepts exactly one variable, while x accepts one or more space-separated variables. Every x enters each group regression, but the coefficient-difference test and permutation test always target the first x. The other non-empty fields accept space-separated values. In absorb, join interacted fixed effects with #, such as analyst#year; cluster lists clustering variables. The program constructs the internal estimation clause from these two fields.

groups accepts one or more space-separated grouping variables. With --merge, | separates output arrays: for example, groups: size attention | portfolio experience produces two merged tables, one for each side of the pipe. A variable that is already binary 0/1 is used directly and ignores the median settings; otherwise, its group is formed by the median. Each space-separated byvars item is one grouping method. # joins keys, so ind#year means joint ind + year grouping, while all means the full sample. Terminal fixed effects, derived-group headings, and seed notices preserve the input # syntax instead of rewriting it as _; ties accepts one or more methods: low assigns median ties to Low; high assigns median ties to High; average(code) stably assigns them in ascending code order; and average(seed) randomly assigns them using --seed. Both average methods keep Low and High within each median scope equal in size or different by at most one. Legacy average remains an alias for average(code). With --seeds, an average(seed) grouping is rebuilt for every retry seed 0–9 before that seed's selected resampling test. The run executes the Cartesian product of the requested byvars and median methods. For example, byvars: ind#year year ind all with ties: average(code) low produces 4 × 2 = 8 results with distinct derived variables and filenames. Missing grouping values or grouping keys are retained in the loaded data but excluded only from the corresponding bdiff. The terminal reports actual regression observations as a percentage of all loaded rows. Each grouping variable selected by the current --export mode normally produces one file named ~/Downloads/<timestamp>_<group-variable>.rtf. Each file is a complete two-column table whose four header rows show y, the italic grouping variable, Low/High, and model numbers in that order. The first row merges every model column and shows y once; its bottom rule spans the complete result area but omits the variable-label column. VARIABLES sits in the first cell of the model-number row. Only header rows two and three use borderless minimum-width spacer columns: row two separates adjacent grouping pairs, while row three separates Low from High and also separates adjacent pairs. The top rule, model-number bottom rule, N top rule, and adjusted-R-squared bottom rule span the entire table; the two middle header rules omit the variable-label and spacer columns. One merged cell after the tested-X t statistic reports Diff. = plus the coefficient difference and its significance stars on the first line, then italic p followed by -value = and the empirical value on a second line without a semicolon. With --merge, the tables within each | array are joined horizontally in input order. One array writes <timestamp>_merged.rtf; multiple arrays write <timestamp>_merged_1.rtf, _merged_2.rtf, and so on. Two grouping variables inside one array therefore produce four consecutively numbered model columns (1)–(4), with a separate difference cell spanning each ordinary Low/High model pair. Every bdiff table uses the shared 14.9 cm total width, gives its centered variable-name column the minimum dynamically calculated width that keeps the longest displayed variable or row label on one line, then divides the remaining width equally across all model columns. With --join, all otherwise selected tables are written to one <timestamp>_joined.rtf; the first table starts on page 1 and every subsequent table starts after a page break. Without --merge, each grouping variable is one page. With both options, each | array is first merged into one horizontal table and those tables are then joined by page. The default --export significant merges only groups whose reported p-value is below 0.10; use --export all to include every successful group in the merged table. RTF export is enabled only when byvars contains exactly one method and ties contains exactly one method; otherwise all export options are ignored and the regressions plus terminal report still run. Use --output DIR to override the output root. All bdiff RTF tables report Constant using the same effective-sample and clustered-standard-error definition as the KJYJ baseline table. With --short, the exported RTF omits only the individual variables listed under controls:, keeps every variable listed under x, the first-X difference row, and Constant, and adds a Controls row immediately above the fixed-effect rows. Its model cells show Yes when the regression includes controls (No when the input controls: field is empty). The first-column labels Constant, Controls, and fixed-effect names are upright; their Yes / No model cells are also upright. N and the R in R² are italic, while the Adj. prefix and superscript ² remain upright. By default, bdiff uses Fisher's permutation test: it keeps the pooled sample fixed and randomly reassigns groups without replacement. --bootstrap matches Stata bdiff, bsample: each repetition first resamples the pooled observations with replacement and then randomly splits that bootstrap sample. The default RTF note starts with 组间系数差异采用Fisher置换检验,随机置换1000次。; in bootstrap mode it instead states the Bootstrap repetition count. Counts are shown without thousands separators, then the note reports italic t statistics and clustering, significance levels, and the grouping rule last. Joint Chinese grouping scopes use an ASCII hyphen (for example, 行业-年度). Grouping-variable names in the note are italic. Visible spaces between Chinese text and adjacent English names or numbers are removed; spaces inside English phrases are retained. A merged table states a shared byvars × ties rule once instead of repeating it for every Low/High model pair. With --seeds, the permutation test starts with seed 42. If a group's empirical p-value is in the inclusive 0.05–0.10 interval, only that group is rerun with seeds 0 through 9. --seeds force removes the upper boundary and reruns every group with a finite seed-42 p-value greater than or equal to 0.05. When at least one group will be retried, the terminal first prints the complete seed-42 result for every group, including groups that do not trigger retries; only then does the seed 0–9 stage begin. If any retry reaches a higher significance tier than the seed-42 result, the complete result with the smallest qualifying retry p-value is used. Thus a one-star retry replaces a nonsignificant seed-42 result, while a one-star seed-42 result still requires a two- or three-star retry. If no retry improves the significance tier, the original seed-42 result is retained. Without --seeds, only the single seed selected by --seed is run. Selecting a non-42 initial seed also disables both retry modes. Before retrying, the terminal lists each triggering group and its original seed-42 p-value. Afterward it reports the count and share of seed 0–9 retries with p < 0.05, the mean p-value, sample standard deviation, minimum, maximum, and selected seed for each group. Retry timing is averaged over the 10 seeds and displayed as average : ...s/seed.

Export format is controlled by --export-format:

# PNG only (default)
uv run regression-monkey config/config.toml --export-format png

# Interactive HTML only
uv run regression-monkey config/config.toml --export-format html

# Both
uv run regression-monkey config/config.toml --export-format both

Configuration file

data = "path/to/data.dta"
y = ["MPATT"]
x = ["ln_info", "ln_quant", "ln_qual"]
controls_test = ["SOE", "Big4", ["ListAge1", "FirmAge1"]]
controls_must = ["Lev", "Size", ["ROA", "ROE"]]
moderators = []  # R only; each z must already be a control variable
mediators = []   # R only; each m must be an existing numeric column and must not be a control variable

output = "outputs"
export_format = "png"   # png | html | both
dpi = 300
fig_width = 14
n_jobs = 0              # 0 = auto-parallel (up to 9 cores)
engine = "r"            # r | stata

Firm_FE   = "code"
Ind_FE    = "ind"
Time_FE   = "year"
Region_FE = "pref"

no_absorb_vce_robust                    = false
no_absorb_vce_cluster_firm              = false
absorb_firm_time_vce_cluster_firm    = true
absorb_firm_indtime_vce_cluster_firm = true
absorb_ind_time_vce_cluster_firm     = true

A complete template is at config/config.example.toml.

The refactored package exposes active modules at package root, such as from regression_monkey import shared, stata, html.

Input data

Supported formats: .dta, .csv, .parquet, .pq.

Multiple y and x values are accepted; all y × x combinations are processed in one run.

Control variable structure

Both controls_test and controls_must support a mixed structure in TOML or the Python API:

Syntax Meaning
"var" single variable
"var1 var2" two flat variables, same as listing them separately
["A", "B"] mutually exclusive alternative group

Semantics differ:

  • controls_test — each plain entry is optional (include or not). An alternative group means at most one of its members is included. Adds a factor of (group_size + 1) to the total spec count.
  • controls_must — each plain entry is always included. An alternative group means exactly one of its members is included. Adds a factor of group_size.

A variable that appears in both lists causes an immediate error.

Predefined fixed-effect specs

The auto mode selects from a catalog of predefined no-FE / FE + VCE combinations. Terminal output shows the Stata-style noabsorb vce(...) or absorb(...) vce(...) form followed by an explicit standard-error label such as Std.Err.=Robust or Std.Err.=cluster(firm); internal keys use underscores.

TOML key Stata equivalent
no_absorb_vce_cluster_firm noabsorb vce(cluster firm)
no_absorb_vce_robust noabsorb vce(robust)
absorb_firm_time_vce_cluster_firm absorb(firm year) vce(cluster firm)
absorb_firm_indtime_vce_cluster_firm absorb(firm i.ind#i.year) vce(cluster firm)
absorb_firm_regiontime_vce_cluster_firm absorb(firm i.region#i.year) vce(cluster firm)
absorb_firm_indtime_regiontime_vce_cluster_firm absorb(firm i.ind#i.year i.region#i.year) vce(cluster firm)
absorb_firm_time_vce_cluster_region absorb(firm year) vce(cluster region)
absorb_firm_time_vce_cluster_ind absorb(firm year) vce(cluster ind)
absorb_firm_time_vce_cluster_firm_time absorb(firm year) vce(cluster firm year)
absorb_ind_region_time_vce_cluster_ind absorb(ind region year) vce(cluster ind)
absorb_ind_time_vce_cluster_firm absorb(ind year) vce(cluster firm)
absorb_firm_time_vce_robust absorb(firm year) vce(robust)
(and robust variants of the above)

The user-facing estimation engines are R/fixest and Stata/reghdfe. The old Python estimation engine and manual --fe mode are no longer supported by the main CLI.

Output structure

Each run creates a timestamped subdirectory:

outputs/20260414_174122/
  config_snapshot.toml
  sig.parquet
  ab_firm_time_cl_firm/
    MPATT_ln_info_ab_firm_time_cl_firm.png
  ab_firm_indtime_cl_firm/
    MPATT_ln_info_ab_firm_indtime_cl_firm.png
  interactive.html          # only in html / both mode

sig.parquet lists all significant specifications across the entire run, sorted by p_value ascending. Fields: Star, coef, p_value, t_value, obs, Y, X, Controls, FE, cluster, Specs, and grouping columns when applicable.

Star values: +3/+2/+1 for positive coefficients significant at 99%/95%/90%; -3/-2/-1 for negative.

Stata engine

Switch to reghdfe by setting engine = "stata" in TOML or passing --engine stata:

uv run regression-monkey config/config.toml --engine stata

The Stata engine also supports subgroup heterogeneity analysis via grouping_variable_by_ind_time, grouping_variable_by_time, or grouping_variable_by_none. For each grouping variable, one extended figure is produced showing the main coefficient curve, b_z=0/1 subgroup curves, and the c.x#c.z interaction coefficient — all in the same PNG. The legacy key grouping_variable remains as an alias for grouping_variable_by_ind_time.

R engine

Switch to the R/fixest implementation by setting engine = "r" in TOML or passing --engine r:

uv run regression-monkey config/config.toml --engine r

The R engine lives in src/regression_monkey/engine/r.py. It writes a narrow handoff dataset containing only the variables needed by the enabled catalog specs, generates one temporary .R script per Y × X × spec, runs it with Rscript, then reads the standard *_results.parquet / *_plot_meta.json contract used by the PNG and HTML exporters.

R mode accepts moderators = ["z1", "z2"] and mediators = ["m1", "m2"] in TOML, or --moderators z1 z2 --mediators m1 m2 on the CLI. Each moderator z must already appear in controls_must or controls_test; otherwise the run fails before estimation. Each mediator m must be an existing numeric column in the data and must not appear in controls_must or controls_test; any overlap raises an error before estimation. The main COEF curve remains the original regression without moderator or mediator terms. For specifications that include moderator z, R additionally estimates an augmented regression with z and x*z, stores the three statistics x*z, z, and x in the standard *_results.parquet, and HTML renders them below COEF in a MODERATORS panel. Significant moderator rows are exported per variable as sig_moderator_<z>.parquet. For every specification, R also estimates one reference regression per mediator m that replaces y with m. The x coefficient statistics are stored in *_results.parquet, HTML renders them in a MEDIATORS panel below MODERATORS, and significant rows are exported as sig_mediator_<m>.parquet. These checks do not change the number of enumerated control specifications.

R mode is accuracy-first. Each specification keeps the same effective sample it would have under per-spec fixest::feols; specifications with identical effective samples share a cached fixest::demean() call, and the absorbed matrix is then reused with lm.fit(). Robust and one-way clustered SE are computed on that absorbed matrix with fixest-matched absorbed-FE degree-of-freedom accounting. Multi-way clustered specs fall back to exact per-spec feols because fixest may apply non-positive-definite VCOV repairs that should not be approximated. Large-sample cache keys are stored as ordinary string values, not R environment variable names, so runs with tens of thousands of rows do not hit R's variable-name length limit.

The effective-sample cache optimization is documented in assets/R引擎样本缓存优化.md.

Configure the R environment

R mode requires a working Rscript and the R package fixest.

# macOS with Homebrew, if R is not installed yet
brew install r

# Install the required R package and optional speed-up packages
Rscript -e 'install.packages(c("fixest", "data.table", "arrow"), repos = "https://cloud.r-project.org")'

# Verify that Regression Monkey can find Rscript and fixest
Rscript -e 'cat(R.version.string, "\n"); stopifnot(requireNamespace("fixest", quietly = TRUE))'

arrow is optional but recommended: when available, the Python side writes the R handoff data as Feather; otherwise it falls back to CSV. data.table is also optional and speeds up CSV reads when Feather is unavailable.

If Rscript is not on PATH, point Regression Monkey at the exact executable:

uv run regression-monkey config/config.toml --engine r --rscript-path /path/to/Rscript

or set it in TOML:

engine = "r"
rscript_path = "/path/to/Rscript"

R mode currently supports catalog auto specs only, so enable at least one spec flag such as no_absorb_vce_robust = true or absorb_firm_time_vce_cluster_firm = true. Subgroup grouping_variable_* plots remain Stata-only. moderators and mediators are R-only. n_jobs = 0 uses 8 R worker processes by default; --n-jobs N or n_jobs = N sets the worker count explicitly.

Redrawing from saved results

Plot from an existing results file without re-running regressions:

# PNG
uv run regression-monkey-plot \
    --results outputs/<timestamp>/foo_results.parquet \
    --meta    outputs/<timestamp>/foo_plot_meta.json \
    --output  outputs/<timestamp>/foo_replot.png

# HTML
uv run regression-monkey-html \
    --results outputs/<timestamp>/foo_results.parquet \
    --meta    outputs/<timestamp>/foo_plot_meta.json \
    --output  outputs/<timestamp>/foo_interactive.html

Pass --keep-temp to the main entry point to retain *_results.parquet and *_plot_meta.json after a run.

Performance

Total spec count = product of per-slot factors:

Slot type Factor
Plain controls_test variable ×2
controls_test alternative group of size g ×(g+1)
Plain controls_must variable ×1
controls_must alternative group of size g ×g

12 plain controls_test variables → 4 096 regressions per spec.

R mode reuses cached demeaned samples for robust and one-way clustered specs, and falls back to exact per-spec fixest::feols for multi-way clustering. Stata mode delegates estimation to reghdfe; plotting remains handled by the shared PNG/HTML exporters.

About

Why not evolve into a monkey? Regression_Monkey runs every regression your referee might ask for — all 2^K control subsets, N-way fixed effects, CGM clustered standard errors — and ships a publication-ready specification curve before the first revision request arrives.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages