Written and maintained by CASRAI Editorial Board
Last updated
recode, replace, and generate can all end up changing what a variable holds in Stata, but they differ on the two things that matter most: whether the result lands in a new variable or overwrites an existing one, and whether the transformation logic is range-based or expression-based. recode is purpose-built for regrouping values into categories and defaults to overwriting the variable you give it; replace always overwrites an existing variable by design; generate always creates a new variable but has no range syntax of its own. Picking the wrong one is how a Likert item, an age variable, or a survey code gets silently destroyed mid-analysis.
Three commands, three different jobs
These commands get confused because all three can appear to do the same thing — turn one set of values into another — but they were not built for the same job:
recodeis a dedicated regrouping command: give it value or range rules and it maps old values to new ones in one call. It is the natural tool for banding a continuous variable, collapsing sparse categories, or reverse-scoring a scale.replaceis a general-purpose command that assigns a new value to an existing variable, typically restricted with anifqualifier. It has no concept of “rules” — you write the logic yourself, oneifcondition at a time.generateisreplace‘s counterpart for a variable that does not exist yet: it creates a new variable from an expression, again usually paired with repeatedif-qualified statements to build up the full mapping.
The practical question is rarely “which command can do this” — usually more than one can. It is “which one keeps my original data recoverable and my rules least error-prone.” Those two questions are answered differently for each command.
A worked example: banding a satisfaction score
Start with a small dataset of a 5-point satisfaction item collected from six respondents:
. list
+-----------------------+
| id satisfaction |
|------------------------|
1. | 1 2 |
2. | 2 1 |
3. | 3 3 |
4. | 4 5 |
5. | 5 4 |
6. | 6 3 |
+-----------------------+
The goal: collapse the 5-point item into three bands — Low (1–2), Medium (3), High (4–5) — without losing the original satisfaction variable.
recode: range rules, in one call, but the overwrite trap
The general form of a recode call is a variable name followed by one or more parenthesized rules, each mapping an old value (or range of values) to a new value:
recode oldvar (rule1) (rule2) ..., generate(newvar)
For the satisfaction example:
. recode satisfaction (1 2 = 1 "Low") (3 = 2 "Medium") (4 5 = 3 "High"), generate(satisfaction_band)
(6 differences between satisfaction and satisfaction_band)
Three things about that syntax are worth internalising before using recode on a variable you have not already backed up:
- Ranges use
#/#, not#-#.recode age (18/29=1) (30/49=2) (50/max=3), generate(agegroup)is valid; a hyphen is not range syntax here.minandmaxare valid endpoints that resolve to the variable’s actual minimum and maximum, which is convenient but means the boundary moves if the underlying data changes. - Values that don’t match any rule are left unchanged, not set to missing. If a rule set only covers 1–5 but the variable also contains a stray 9 (a common “not applicable” code), that 9 passes through untouched into the recoded variable unless a catch-all rule is added:
(else=.)or(*=.). This is the most common silent error in arecodecall — the output looks complete because nothing errors, but an unhandled code has quietly survived the transformation. - You can attach a value label to a category directly inside the rule, as in the example above (
"Low","Medium","High") —recodedefines and applies the label automatically whengenerate()is used, saving a separatelabel define/label valuesstep.
The overwrite trap is the generate() option itself: it is optional, not default. Running recode satisfaction (1 2 = 1) (3 = 2) (4 5 = 3) with no generate() overwrites satisfaction in place, permanently, the moment the command finishes — there is no confirmation prompt. The only way back at that point is reloading a saved copy of the dataset. Treat generate() as effectively mandatory on any recode call you run against data you have not just saved.
generate: full control, no range syntax, and the missing-value gap
Where recode gives you range rules for free, generate gives you none — you build the same three-band variable from scratch using an expression and a sequence of if-qualified statements:
. generate satisfaction_band = 1 if satisfaction = 4 & satisfaction != .
The first line is the gap that trips people up: generate newvar = expr if condition only ever populates observations where condition is true. Every observation where it is false — including any observation with a missing satisfaction value, since a missing value fails every numeric comparison in Stata — gets a missing value in satisfaction_band, silently, unless a later replace line explicitly handles it. This is the inverse of the recode gotcha above: recode‘s failure mode is an unmatched value passing through unchanged; a hand-built generate/replace chain’s failure mode is an unhandled value defaulting to missing. Both are easy to miss because neither one throws an error.
generate earns its place over recode when the new variable is not a simple regrouping of one existing variable — a score built from arithmetic across several variables, or a condition that depends on more than one field (generate at_risk = 1 if age > 65 & comorbidity_count >= 2) is a generate job, not a recode job, since recode‘s rules only ever look at the single variable being recoded.
replace: the command that only ever overwrites
replace has no “safe mode” — by design, it always modifies an existing variable’s values in place:
replace varname = expr if condition
That makes it the right tool for exactly two situations: fixing values within a variable you already know you want to keep editing (correcting a data-entry error, filling in a derived variable you just created with generate, as in the example above), and never the right tool for transforming a variable you still need in its original form. Two specific risks are worth stating plainly:
- Running
replacedirectly on a raw, original variable is irreversible in the same wayrecodewithoutgenerate()is — the prior values are gone from the dataset in memory the instant the command runs, with no built-in undo. The safe pattern is always togeneratea working copy first, thenreplacethe copy. - An omitted or wrong
ifqualifier overwrites every observation, not just the ones intended.replace satisfaction_band = 3 if satisfaction >= 4is not the same statement asreplace satisfaction_band = 3 if satisfaction >= 4 & satisfaction != .if there happens to be a coding convention where missing is stored as a large sentinel value rather than Stata’s native missing — a straysatisfaction >= 4match on an unexpected value silently reassigns it too.
Choosing between the three
| Situation | Best tool | Why |
|---|---|---|
| Banding or regrouping one existing variable by value/range | recode, with generate() |
Built-in range syntax and inline labels, in one call |
| A new variable computed from an expression or multiple variables | generate + replace |
recode‘s rules only see one source variable |
| Correcting values in a variable you already intend to keep editing | replace |
That is exactly what it is for — just never on the only copy of raw source data |
| Any of the above on a variable you have not backed up | None of them, without preserve first or a saved copy of the dataset |
All three can destroy the original values with no confirmation prompt |
Frequently asked questions
Does recode work on string variables?
No — recode‘s rules operate on numeric values. A string variable needs to be converted with encode first (which also assigns the underlying numeric codes a value label), and the resulting numeric variable can then be recoded normally.
What happens if I run recode twice on the same generate() target name?
Stata refuses, the same as any other command that would create a variable that already exists — you get an “already defined” error rather than a silent overwrite. Drop the variable first (drop satisfaction_band) or add the replace option to the generate() call itself if you are intentionally redoing the recode.
Is there a way to preview a recode before committing to it?
Wrap the command in preserve and restore: run preserve, then the recode, then tabulate oldvar newvar to check the cross-tabulation matches intent, then restore to discard the change and rerun it for real once it looks correct. This is the same non-destructive-preview pattern documented for the collapse command in Stata, since neither command has a built-in preview mode.
How does this compare to recoding in SPSS?
The underlying decision is identical — write the recoded result to a new variable rather than overwriting the source — but the mechanics differ. SPSS separates this into two distinct menu commands, Recode into Same Variables vs. Recode into Different Variables; Stata’s recode is a single command where the same choice is made by including or omitting the generate() option.
Related reading
Once a variable is recoded, labelling the resulting categories clearly matters for anyone reading the output later — see labelling variables and values in Stata for the full label define/label values workflow beyond the inline labels shown above. For the statistical reason a banded variable behaves differently from a continuous one in a model, see levels of measurement. For choosing Stata over the alternatives in the first place, see CASRAI’s SPSS vs. Stata comparison and R vs. Stata comparison. For the broader landscape of tools covered here, see the Research Tools & Software hub.








