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 = x

R 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.2f

R 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^2

R 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 = _N

R 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 = _n

R Package:dplyr

mutate(data, n = row_number())

Days between two dates

Stata

generate days = d2 - d1

R 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 > 0

R 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/20

R 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 x

R 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 x2

R 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 * *_old

R Package:dplyr

rename_with(data, ~ paste0(.x, "_old"))

Make all names lower case

Stata

rename *, lower

R Package:dplyr

rename_with(data, tolower)

Upper-case some names

Stata

rename x*, upper

R Package:dplyr

rename_with(data, toupper, starts_with("x"))

replace

Change a variable

Stata

replace x = x^2

R 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 > 0

R 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/20

R 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))