fvgen
Data PreparationLabeled variables from factor-variable interactions
Version 1.0.0 | 2026-10-15
Quick Start · Installation · Worked Examples · Key Options · Stata-Tools · Report a bug
fvgen turns Stata factor-variable specifications into ordinary, labeled main-effect and interaction variables for regression tables and other exports. It returns a ready-to-use r(allvars) varlist while preserving the estimable design of the native model.
Quick Start
After installing, flatten a categorical-by-continuous interaction and fit the same model on the labeled variables:
sysuse auto, clear
regress price i.foreign##c.mpg
fvgen i.foreign##c.mpg
regress price `r(allvars)'
The same coefficients, exported from each fit's r(table):
Before — regress price i.foreign##c.mpg
| Term | Coef. | 95% CI | p |
|---|---|---|---|
| 0b.foreign | (base) | ||
| 1.foreign | -14 | (-5268, 5241) | 0.996 |
| mpg | -329 | (-479, -180) | 0.000 |
| 0b.foreign#co.mpg | (base) | ||
| 1.foreign#c.mpg | 79 | (-145, 303) | 0.485 |
| Intercept | 12601 | (9553, 15648) | 0.000 |
After — fvgen i.foreign##c.mpg; regress price r(allvars)
| Term | Coef. | 95% CI | p |
|---|---|---|---|
| Foreign | -14 | (-5268, 5241) | 0.996 |
| Mileage (mpg) | -329 | (-479, -180) | 0.000 |
| Foreign × Mileage (mpg) | 79 | (-145, 303) | 0.485 |
| Intercept | 12601 | (9553, 15648) | 0.000 |
From demo/export_comparison.md, written by demo/demo_fvgen.do, whose small table helper renders each row from r(table) using the variable label (after) or the coefficient name (before). Run fvgen, drop when you are finished.
Features
- Labels from your data. Value labels become variable labels, interaction labels join their parts with
×(orxsymbol()), andvsref()can name the reference level in each main-effect label. - The same model, flattened. In the default uncentered workflow, a fit on
r(allvars)reproduces the native factor-variable model's fit; full-rank designs also reproduce each coefficient and standard error. - Nothing overwritten by accident. Before changing the dataset,
fvgenrejects plans in which two terms map to one generated name or an output would overwrite a source variable. - Clean teardown. Generated variables carry provenance characteristics, so
fvgen, dropremoves only whatfvgencreated and leaves pass-through originals,clonevarcopies and renamed variables alone. - Back to
margins.fvgen, marginsrebuilds the fit with native factor variables formargins, and refuses (r(498)) when the data or the model no longer match.
Requirements
- Stata 16.0 or later
- No additional Stata packages or external software
Installation
capture ado uninstall fvgen
net install fvgen, from("https://raw.githubusercontent.com/tpcopeland/Stata-Tools/main/fvgen") replace
For a local Stata-Tools checkout, use the same command with from("/path/to/Stata-Tools/fvgen").
Commands
| Command | Description |
|---|---|
fvgen |
Flatten factor-variable terms, remove generated variables, or rebuild a margins-ready estimate |
How It Works
fvgen expands i. factors into indicator variables, passes continuous terms through as ordinary variables, and turns # or ## interactions into products. The default base level is omitted for categorical main effects; value labels become variable labels, and generated names use a prefix plus the source variable and level. Before changing the dataset, it rejects output plans in which distinct terms collapse to one generated name or an output would overwrite a source variable.
The command supports main effects and up to two-way interactions. It returns a combined r(allvars) varlist in estimation order, along with separate main-effect, interaction, and newly generated-variable lists. Generated variables carry fvgen_role, fvgen_term, and fvgen_name characteristics so fvgen, drop can remove only variables created by fvgen that still have the name fvgen gave them; a clonevar copy or a renamed generated variable is kept. Dataset-level provenance conservatively signs every variable present when fvgen runs, plus its generated variables, so the margins bridge can reject stale data.
The if or in qualifier and any weight control which levels and interaction cells are discovered, while generated variables are filled for all observations. To reproduce the native model for a restricted sample, use the same qualifiers and weights in the estimation command. Source-variable missing values remain missing in the generated indicators and products. With center, the weight also determines the centering mean.
In the default uncentered workflow, estimating on r(allvars) spans the same model space and reproduces the corresponding native factor-variable model's fit. When the design is full rank, matching terms also have the same coefficients and standard errors. With empty cells or other exact collinearity, Stata may choose a different omitted-column basis, so individual reported coefficients and standard errors need not map one-to-one even though fitted values and fit agree. Centering changes the interpretation of lower-order coefficients but leaves the interaction coefficient and model fit unchanged.
Official xi also creates indicator and product variables (xi i.foreign*mpg makes _Iforeign_1 and _IforXmpg_1, labeled foreign==1 and (foreign==1)*mpg). fvgen adds labels built from value labels, modern factor-variable syntax (ibN., ibn., c., ##), per-factor reference levels and per-group slopes, provenance with a matching teardown, and a bridge back to native factor variables for margins.
Worked Examples
1. Categorical-by-continuous interaction
The standard workflow creates a labeled indicator and product, then passes the returned varlist to the estimator.
sysuse auto, clear
fvgen i.foreign##c.mpg
regress price `r(allvars)'
fvgen, drop
2. Reference level and comparison labels
ref() changes the base used for a factor, while vsref() makes that comparison visible in main-effect labels.
sysuse auto, clear
fvgen i.foreign##i.rep78, ref(rep78 3) vsref("(vs. @)")
regress price `r(allvars)'
3. Centered continuous interaction
center creates centered copies before forming the product; xsymbol(x) requests an ASCII interaction label.
sysuse auto, clear
fvgen c.mpg##c.weight, center xsymbol(x)
regress price `r(allvars)'
4. Per-group slopes
With simple(foreign), the continuous slope is reported separately within each level of the categorical moderator instead of as a reference slope plus a difference.
sysuse auto, clear
fvgen i.foreign##c.mpg, simple(foreign)
regress price `r(allvars)'
5. Margins after a flattened regression
store() preserves the active flattened estimate for export and saves a native factor-variable clone for margins.
sysuse auto, clear
fvgen i.foreign##c.mpg
regress price `r(allvars)'
fvgen, margins store(m_price)
estimates restore m_price
margins, dydx(mpg) at(foreign=(0 1))
Demo
The checkout demo compares native and flattened coefficient tables for i.foreign##c.mpg and i.foreign##i.rep78. From a Stata-Tools checkout root, run:
stata-mp -b do fvgen/demo/demo_fvgen.do
The script installs the local package and regenerates demo/export_comparison.md; it is a checkout workflow, not part of the net install payload. The categorical-by-categorical example contains empty cells, so Stata may select a different omitted-column basis across equivalent fits; treat its individual coefficients as presentation examples, not a one-to-one parity check.
Command Reference
Main generation mode
fvgen fvvarlist [if] [in] [weight] [, alllevels center prefix(name) ref(spec) simple(varname) vsref(string) replace xsymbol(string)]
fvvarlist uses Stata's i., c., #, and ## operators. The supported weights are aweight, fweight, pweight, and iweight; they define the sample used to discover levels and cells and, with center, determine the centering mean.
Margins bridge
fvgen, margins [store(name) replace]
Use this after estimating with the exact varlist returned in r(allvars). Without store(), the native factor-variable refit becomes active. With store(name), the refit is stored and the flattened estimate is restored. If any variable present when fvgen ran, or any generated variable, changed afterward, the bridge exits with error 498; rerun fvgen and the flattened estimator first. A variable added later does not invalidate the bridge. A replay error or nonconvergence also restores the original active estimate.
Teardown mode
fvgen, drop
This mode takes no varlist, qualifiers, weights, or other generation options. It drops every variable tagged as generated by fvgen that still carries its generated name; clonevar copies and renamed generated variables are kept, and pass-through originals are untouched.
Key Options
| Option | Default | Use and limits |
|---|---|---|
alllevels |
Off | Materialize the base level in categorical main effects; interaction terms still use estimable cells. |
center |
Off | Center continuous terms over the marked sample before forming products; a weight defines that sample and determines the mean. The margins bridge is unavailable after centering. |
ref(spec) |
Stata factor-variable base | Set bases with variable/level pairs such as ref(sex 2, race 3); quoted tokens resolve as value-label strings even when numeric, ambiguous duplicate labels are rejected, and levels must be observed in the marked sample. |
simple(varname) |
Off | Report each interacting continuous term as a slope within levels of varname; varname must be a factor and categorical-by-categorical simple effects are not supported. |
vsref(string) |
Off | Append the base label to categorical main-effect labels; the template must contain @, and the displayed base honors ref(). |
prefix(name) |
_ |
Prefix generated names; a name longer than Stata's 32-character limit is an error. |
replace |
Off | Overwrite unrelated existing variables whose names collide with generated names; structural output/output and output/source collisions are always rejected. With margins store(name), refresh an existing stored clone; without replace, an existing stored estimate of that name is an error (r(110)). |
xsymbol(string) |
× |
Set the symbol joining interaction labels; xsymbol(x) uses ASCII; a continuous self-interaction is labeled with ², or ^2 when xsymbol() is plain ASCII. |
margins |
Off | Rebuild the active estimate with native factor-variable syntax for Stata's margins command. |
store(name) |
Not used | Use only with margins to store the native refit under name and restore the flattened estimate. |
drop |
Off | Use alone to remove all fvgen-generated variables still under their generated names and return their names and count; clonevar copies and renamed generated variables are kept. |
Stored Results
For ordinary generation, fvgen returns these scalars:
| Result | Description |
|---|---|
r(k_all) |
Number of variables in r(allvars) |
r(k_main) |
Number of main-effect variables |
r(k_int) |
Number of interaction variables |
For ordinary generation, it also returns these macros:
| Result | Description |
|---|---|
r(spec) |
Effective expanded factor-variable specification, including ref() bases and any simple() reparameterization |
r(allvars) |
All model variables in estimation order |
r(mainvars) |
Main-effect variables only |
r(intvars) |
Interaction variables only |
r(genvars) |
Newly created variables, excluding pass-through originals |
With fvgen, drop, the returned results are r(k_dropped) (a scalar count) and r(dropped) (the dropped variable names). With fvgen, margins, r(margins) is active or stored, and r(stored) contains the estimate name when store() was used.
The native-factor result produced by fvgen, margins also carries these nonstandard e() macros (including in an estimate saved with store(name)):
| Result | Description |
|---|---|
e(fvgen_margins) |
Marks the result as the margins-ready native-factor clone (1) |
e(fvgen_flat_cmdline) |
Original estimation command using the flattened variables |
e(fvgen_native_cmdline) |
Reconstructed estimation command using native factor-variable syntax |
Assumptions and Limits
- Higher-order interactions with three or more factors are rejected; use a native factor-variable model or split the workflow.
- The explicit omit operator
o.is rejected becausefvgencannot infer whether it should be materialized; restrict the sample withiforin, or set a base withref()instead. - A no-base factor such as
ibn.foreignmaterializes every observed level, equivalent toalllevelsfor that factor. Empty cells and omitted interaction terms are not materialized. Naming anibn.factor inref()is an error (r(198)). - Generated variable names must fit Stata's 32-character limit, and generated variable labels are truncated at Stata's 80-character limit.
- The
marginsbridge requires active estimation results withe(b),e(V), and a saved command line, plus currentfvgenprovenance from the exactr(allvars)varlist. Changing, dropping, or recasting any variable present whenfvgenran, or any generated variable, invalidates the bridge; adding a variable afterward does not. The estimator must be rerunnable and converge with native factor variables and supportmargins, and the native refit must be the same model as the flattened fit (same estimation sample, N, rank, free coefficients, log likelihood where posted, and variance matrix); a mismatch, e.g. after runningfvgenon a differentif/insample than the estimator or withalllevelsplusnoconstant, exits with r(498). Bootstrap and jackknife variances (bootstrap:prefix orvce(bootstrap)) are replayed from the fit's postede(rngstate)so the native refit redraws the same replicates, then checked against the flattened variance; the caller's random-number state is left unchanged. A resampling variance is refused (r(498)) when the generator type was changed after the fit (set rng) or when exact collinearity, such asalllevelswith a constant, gives the native refit a different omitted-column basis; fit such models natively. Because the bridge reruns the saved command line, a command line withsaving()or any othersav*()option (includingbootstrap, saving(),jackknife, saving(), andvce(bootstrap, saving())) is refused with r(198) so the replay never rewrites that file, andmi estimateresults are refused with r(198); refit withoutsaving()or fit natively; a user-written estimator that writes files without asav*()option is not detected. A failed replay shows the estimator's own error message. Use the native model directly forcontrastandpwcompare; the bridge is not available aftercenter.
References
- Stata factor-variable syntax:
help fvvarlist - Estimation and postestimation:
help regressandhelp margins - Variable and value labels:
help label
QA
QA suites and how to run them are documented in qa/README.md.
Version History
- 1.0.0 (2026-10-15): Initial release.
Author
Timothy P Copeland, Karolinska Institutet
Questions and bug reports: GitHub issues. Please include the first line of which fvgen and your Stata version.
To cite: Copeland TP. fvgen: Labeled variables from factor-variable interactions. Stata-Tools, https://github.com/tpcopeland/Stata-Tools/tree/main/fvgen. Please give the version you used (which fvgen prints it).
License
MIT (see LICENSE)