Construct and modify variables
Start with the expanded example for each command. Open the variants for options and alternative R approaches. Read the qualifications before adapting the code.
ceil
Round up
Stata
generate y = ceil(x)R Package:base
mutate(data, y = ceiling(x))clonevar
Copy a variable
Stata
clonevar x2 = xR Package:dplyr
mutate(data, x2 = x)Differs from Stata mutate(x2 = x) copies the R vector and attributes, but does not reproduce all Stata clonevar characteristics and notes.
collapse
Replace the data with group means
Stata
collapse (mean) x1 x2, by(group)R Package:dplyr
summarise(data, across(c(x1, x2), ~ mean(.x, na.rm = TRUE)), .by = group)Differs from Stata Assign the result to replace the data, as collapse does: data <- summarise(…). summ(data, x1, x2, .by = group) shows a summary table instead. R retains missing grouping keys; Stata tabstat by() and tabulate summarize() exclude missing grouping categories unless requested. Check the intended grouping sample.
1 more examples and alternatives
Different statistics for different variables
Stata
collapse (count) n = x (sum) total = y, by(group)R Package:dplyr
summarise(data, n = sum(!is.na(x)), total = sum(y, na.rm = TRUE), .by = group)cond
One value if a condition holds, another if not
Stata
generate y = cond(x > 0, 1, 0)R Package:dplyr
mutate(data, y = if_else(x > 0, 1, 0))Differs from Stata With missing x, Stata’s cond() takes the first value (missing counts as large); if_else() returns NA. Stata numeric missing values compare larger than every finite number; R comparisons return NA. Handle missing values explicitly before applying the condition.
destring
Text to numbers
Stata
destring str_x, generate(num_x)R Package:tidystata
mutate(data, num_x = destring(str_x))Differs from Stata Text that is not a number becomes NA with a warning, as with force. destring(str_x, ignore = “kg”) removes those characters first, like ignore(), so “10 kg” gives 10. Invalid numeric text becomes NA with a warning here; Stata destring without force refuses conversion of a variable containing such text.
egen
Ten groups of about equal size
Stata
egen bins = cut(x), group(10)R Package:tidystata
mutate(data, bins = xtile(x, 10))Differs from Stata egen cut(), group() has its own grouping rule; xtile() follows Stata’s xtile, the usual command for quantile groups.
Mark the first row of each group
Stata
egen first = tag(group)R Package:tidystata
mutate(data, first = is_first(group))Differs from Stata Gives TRUE/FALSE; rows with a missing group are FALSE, as in Stata, unless .missing = TRUE. The R result is logical TRUE/FALSE rather than numeric 1/0; use as.integer() when the stored type matters.
Number the groups
Stata
egen id = group(g1 g2)R Package:dplyr
data |> group_by(g1, g2) |> mutate(id = cur_group_id()) |> ungroup()Differs from Stata cur_group_id() also numbers groups with missing keys; egen group() excludes those rows without the missing option. String collation can change ID ordering.
Sample statistic as a new variable
Stata
egen x_avg = mean(x)R Package:dplyr
mutate(data, x_avg = mean(x, na.rm = TRUE))Differs from Stata R functions keep missing values unless na.rm = TRUE; Stata’s egen ignores them. For an all-missing group mean(…, na.rm = TRUE) gives NaN; Stata egen mean() gives numeric missing. Normalize NaN if that distinction matters.
Row total
Stata
egen x_sum = rowtotal(x1-x5)R Package:tidystata
mutate(data, x_sum = row_sum(x1:x5))24 more examples and alternatives
Group median
Stata
bysort group: egen med = median(x)R Package:dplyr
mutate(data, med = median(x, na.rm = TRUE), .by = group)Group minimum
Stata
bysort group: egen mn = min(x)R Package:dplyr
dplyr::mutate(data, mn = if (all(is.na(x))) NA_real_ else min(x, na.rm = TRUE), .by = group)Differs from Stata All-missing groups return NA without evaluating min() on an empty set. All-missing groups produce -Inf/Inf and a warning in R max/min with na.rm = TRUE; Stata egen max()/min() returns missing.
Group maximum
Stata
bysort group: egen mx = max(x)R Package:dplyr
dplyr::mutate(data, mx = if (all(is.na(x))) NA_real_ else max(x, na.rm = TRUE), .by = group)Differs from Stata All-missing groups return NA without evaluating max() on an empty set. All-missing groups produce -Inf/Inf and a warning in R max/min with na.rm = TRUE; Stata egen max()/min() returns missing.
Number of observed values in each group
Stata
bysort group: egen n = count(x)R Package:dplyr
mutate(data, n = sum(!is.na(x)), .by = group)Group standard deviation
Stata
bysort group: egen s = sd(x)R Package:dplyr
mutate(data, s = sd(x, na.rm = TRUE), .by = group)Group total
Stata
bysort group: egen tot = total(x)R Package:dplyr
mutate(data, tot = sum(x, na.rm = TRUE), .by = group)Group statistic as a new variable
Stata
bysort group: egen x_avg = mean(x)R Package:dplyr
mutate(data, x_avg = mean(x, na.rm = TRUE), .by = group)Differs from Stata For an all-missing group mean(…, na.rm = TRUE) gives NaN; Stata egen mean() gives numeric missing. Normalize NaN if that distinction matters.
Groups by given cutpoints
Stata
egen bins = cut(x), at(0(10)100)R Package:base
mutate(data, bins = cut(x, breaks = seq(0, 100, 10), right = FALSE))Differs from Stata right = FALSE makes each interval include its lower cutpoint, as egen cut() does. cut_length(x, 10) instead splits the range into 10 equal widths. cut() returns a factor of interval labels; egen cut() stores numeric lower endpoints even when interval closure matches.
First observed value across variables
Stata
egen first = rowfirst(x1-x5)R Package:dplyr
mutate(data, first = coalesce(x1, x2, x3, x4, x5))Join variables into one string
Stata
egen id = concat(a b), punct(_)R Package:base
mutate(data, id = paste(a, b, sep = "_"))Differs from Stata paste() turns NA into the text “NA”; egen concat() follows Stata missing-value and numeric-display conventions.
Last observed value across variables
Stata
egen last = rowlast(x1-x5)R Package:dplyr
mutate(data, last = coalesce(x5, x4, x3, x2, x1))Count how many variables equal a value
Stata
egen n9 = anycount(x1-x5), values(9)R Package:dplyr
mutate(data, n9 = rowSums(across(x1:x5, ~ .x %in% 9)))Percentage of the total
Stata
egen p = pc(x)R Package:tidystata
mutate(data, p = pc(x))pc(x, prop = TRUE) gives proportions, like the prop option.
Rank, with ties averaged
Stata
egen r = rank(x)R Package:base
mutate(data, r = rank(x, na.last = "keep"))rank() gives tied values their average rank, like egen rank(); dplyr::min_rank() gives them the lowest.
Repeating sequence
Stata
egen s = seq(), from(1) to(3)R Package:dplyr
mutate(data, s = rep_len(1:3, n()))Row mean
Stata
egen x_avg = rowmean(x1-x5)R Package:tidystata
mutate(data, x_avg = row_mean(x1:x5))Row maximum
Stata
egen x_max = rowmax(x1-x5)R Package:tidystata
mutate(data, x_max = row_max(x1:x5))Row median
Stata
egen x_med = rowmedian(x1-x5)R Package:tidystata
mutate(data, x_med = row_median(x1:x5))Number of missing values in each row
Stata
egen x_mi = rowmiss(x1-x5)R Package:tidystata
mutate(data, x_mi = row_miss(x1:x5))Row minimum
Stata
egen x_min = rowmin(x1-x5)R Package:tidystata
mutate(data, x_min = row_min(x1:x5))Number of observed values in each row
Stata
egen x_n = rownonmiss(x1-x5)R Package:tidystata
mutate(data, x_n = row_non_miss(x1:x5))Number of distinct values in each row
Stata
egen x_nv = rownvals(x1-x5)R Package:tidystata
mutate(data, x_nv = row_nvals(x1:x5))From egenmore (SSC). Missing values are ignored unless .missing = TRUE, like the missing option.
Row standard deviation
Stata
egen x_sd = rowsd(x1-x5)R Package:tidystata
mutate(data, x_sd = row_sd(x1:x5))Standardise a variable
Stata
egen z = std(x)R Package:tidystata
mutate(data, z = std(x))Base R: as.vector(scale(x)). Add .by = g for by g: egen.
floor
Round down
Stata
generate y = floor(x)R Package:base
mutate(data, y = floor(x))format
Display numbers with two decimals
Stata
format x %9.2fR Package:base
mutate(data, x_text = sprintf("%.2f", x))R stores the number unchanged and formats it only when printing, for example with round(x, 2) or sprintf().
generate
Create a variable
Stata
generate x2 = x^2R Package:dplyr
mutate(data, x2 = x^2)Assign the result to keep it: data <- mutate(data, …). R has no separate generate and replace.
8 more examples and alternatives
Number of rows
Stata
generate N = _NR Package:dplyr
mutate(data, N = n())Add .by = group for the number of rows in each group, like bysort group: generate N = _N.
Row number
Stata
generate n = _nR Package:dplyr
mutate(data, n = row_number())Days between two dates
Stata
generate days = d2 - d1R Package:base
mutate(data, days = as.numeric(d2 - d1))Dummy variable from a condition
Stata
generate dummy = (x1 == 1 & x2 == 2)R Package:dplyr
dplyr::mutate(data, dummy = as.integer(x1 %in% 1 & x2 %in% 2))Differs from Stata Stata numeric equality is false for missing operands; %in% keeps the dummy zero when either input is missing. The R result is logical TRUE/FALSE rather than numeric 1/0; use as.integer() when the stored type matters.
Value from the previous row
Stata
generate lag_x = x[_n-1]R Package:dplyr
mutate(data, lag_x = lag(x))This shifts rows, like x[_n-1], and ignores gaps in time. See L. for panel lags.
Value from two rows ahead
Stata
generate lead_x = x[_n+2]R Package:dplyr
mutate(data, lead_x = lead(x, 2))Create a variable under a condition
Stata
generate x2 = x^2 if x > 0R Package:dplyr
mutate(data, x2 = if_else(x > 0, x^2, NA))In Stata missing is larger than any number, so x > 0 is true for missing x. In R the condition is NA and so is the result.
Create a variable for some rows
Stata
generate x2 = x^2 in 11/20R Package:dplyr
mutate(data, x2 = if_else(row_number() %in% 11:20, x^2, NA))inlist
Whether x equals any listed value
Stata
generate y = inlist(x, 1, 3, 5)R Package:base
mutate(data, y = x %in% c(1, 3, 5))Differs from Stata %in% gives FALSE for missing x, as inlist() does. R returns a logical indicator rather than the numeric 1/0 stored by Stata generate; use as.integer() if the stored type matters.
inrange
Whether x lies within a range
Stata
generate y = inrange(x, 1, 5)R Package:dplyr
mutate(data, y = between(x, 1, 5))Differs from Stata between() returns NA for missing x, whereas Stata’s inrange() returns 0.
ln
Natural logarithm
Stata
generate y = ln(x)R Package:base
mutate(data, y = log(x))Differs from Stata log10() matches Stata’s log10(). R’s log(0) is -Inf rather than missing. R log(0) gives -Inf and a negative input gives NaN with a warning; Stata ln() gives numeric missing for these invalid inputs.
missing
Whether a value is missing
Stata
generate m = missing(x)R Package:base
mutate(data, m = is.na(x))Differs from Stata R returns a logical indicator rather than the numeric 1/0 stored by Stata generate; use as.integer() if the stored type matters.
mvdecode
Turn missing-value codes into missing
Stata
mvdecode x, mv(-9)R Package:tidystata
mutate(data, x = mvdecode(x, -9))mvdecode(x, c(-8, -9)) handles several codes, like mv(-8 -9).
mvencode
Turn missing values into a code
Stata
mvencode x, mv(-9)R Package:tidystata
mutate(data, x = mvencode(x, -9))pctile
Quantile cutpoints
Stata
pctile p = x, nquantiles(4)R Package:tidystata
summ(data, x, .stat = c("p25", "median", "p75"))Differs from Stata summ() uses Stata’s percentile rule. quantile(x, type = 2) is close, but treats some whole positions, such as 100 * 0.3, as fractional. summ() returns percentile values in a summary table; pctile creates a dataset column containing the cutpoints.
recast
Change the storage type
Stata
recast double xR Package:base
mutate(data, x = as.double(x))R chooses storage automatically, so there is no compress either.
recode
Recode ranges of values
Stata
recode x (1/5 = 1) (6/10 = 2) (11/15 = 3) (else = .), generate(x3g)R Package:dplyr
mutate(data, x3g = case_when(between(x, 1, 5) ~ 1, between(x, 6, 10) ~ 2, between(x, 11, 15) ~ 3))Values that match no condition become NA, like else = . in Stata.
1 more examples and alternatives
Turn one code into missing
Stata
recode x (9 = .)R Package:dplyr
mutate(data, x = na_if(x, 9))rename
Rename a variable
Stata
rename x x2R Package:dplyr
rename(data, x2 = x)Note the order: new name = old name.
4 more examples and alternatives
Rename several variables
Stata
rename (a b) (c d)R Package:dplyr
rename(data, c = a, d = b)Add a suffix to every name
Stata
rename * *_oldR Package:dplyr
rename_with(data, ~ paste0(.x, "_old"))Make all names lower case
Stata
rename *, lowerR Package:dplyr
rename_with(data, tolower)Upper-case some names
Stata
rename x*, upperR Package:dplyr
rename_with(data, toupper, starts_with("x"))replace
Change a variable
Stata
replace x = x^2R Package:dplyr
mutate(data, x = x^2)2 more examples and alternatives
Change a variable under a condition
Stata
replace x = x^2 if x > 0R Package:dplyr
mutate(data, x = if_else(x > 0, x^2, x))Differs from Stata Stata numeric missing values compare larger than every finite number; R comparisons return NA. Handle missing values explicitly before applying the condition.
Change a variable for some rows
Stata
replace x = x^2 in 11/20R Package:dplyr
mutate(data, x = if_else(row_number() %in% 11:20, x^2, x))rnormal
Normal random numbers
Stata
generate z = rnormal()R Package:stats
mutate(data, z = rnorm(n()))Differs from Stata R and Stata use different random-number streams; the same numeric seed does not reproduce the same draws or selected rows.
round
Round
Stata
generate y = round(x, 0.1)R Package:base
mutate(data, y = round(x, 1))Differs from Stata R’s second argument is the number of decimals. R rounds halves to even (round(0.5) is 0); Stata rounds them up.
runiform
Uniform random numbers
Stata
generate u = runiform()R Package:stats
mutate(data, u = runif(n()))Differs from Stata R and Stata use different random-number streams; the same numeric seed does not reproduce the same draws or selected rows.
sum
Running sum
Stata
generate cs = sum(x)R Package:dplyr
mutate(data, cs = cumsum(coalesce(x, 0)))Stata’s sum() function treats missing values as 0, hence coalesce(x, 0).
tabulate
Dummy variables for every category
Stata
tabulate x, generate(x_)R Package:tidystata
as_dummy(data, x)Differs from Stata as_dummy(data, x, .names = “index”) numbers the columns x_1, x_2, … as Stata does. Also: fastDummies::dummy_cols(data, “x”). as_dummy() defaults to value-based column names; .names = “index” gives numbered Stata-style suffixes. Check the missing-category indicator policy too.
tostring
Numbers to text
Stata
tostring num_x, generate(str_x)R Package:tidystata
mutate(data, str_x = tostring(num_x))Differs from Stata Labelled values keep their codes; decode() gives the value labels instead. Formatting and retained precision can differ from Stata tostring; choose an explicit format when exact text or round-trip precision matters.
winsor2
Winsorise at the 1st and 99th percentiles
Stata
winsor2 x, cuts(1 99)R Package:datawizard
mutate(data, x_w = datawizard::winsorize(x, threshold = 0.01))Differs from Stata datawizard uses R quantile cutpoints while winsor2 uses Stata percentile rules; small samples and ties can therefore yield different caps.
xtile
Quantile groups
Stata
xtile q = x, nquantiles(4)R Package:tidystata
mutate(data, q = xtile(x, 4))xtile() follows Stata’s rule, so equal values stay in the same group.
1 more examples and alternatives
Weighted quantile groups
Stata
xtile q = x [aweight = w], nquantiles(4)R Package:tidystata
mutate(data, q = xtile(x, 4, wt = w))