Skip to contents

jrecode() recodes a variable using a simple map string that specifies how old values should be converted to new values. It is designed for situations where you need to collapse categories, change numeric codes, or recode dichotomies. Variable and value labels are handled automatically.

Map and labels rules can also produce missing values: plain system NA via the NA / System / SYSMIS aliases, or tagged missing values (.a through .z, or .A through .Z) when the active convention is Stata or SAS. See Missing values in the map below for the canonical patterns under each convention.

Usage

jrecode(data, orig.var, map, labels = NULL, convention = NULL, ...)

Arguments

data

A data frame containing the original variable.

orig.var

The variable to recode (unquoted, e.g. AgeGroup; a quoted name is accepted).

map

A quoted string specifying the recode rules, using the format "old=new" with rules separated by semicolons. Multiple old values mapping to the same new value are separated by commas on the left side.

An optional else clause controls what happens to values not covered by the map:

  • No else clause: the function stops with a message if any values are left unmapped, so you can fix the map before proceeding.

  • else=NA (also else=System or else=SYSMIS): unmapped values are deliberately set to system NA.

  • else=copy: unmapped values are carried across unchanged.

  • else=.a (or any other marker, .a through .z; Stata or SAS convention only): unmapped values are set to that Stata-style or SAS-style missing value.

Individual values can also be mapped to system NA using the same aliases: "-5=NA", "-5=System", or "-5=SYSMIS".

The same aliases may also appear as an OLD value, converting plain NA cells to a code: "NA=-98; else=copy" recodes every NA to -98 (declare the code afterward with jdeclare_missing()). NA may be combined with numeric old values in one rule ("NA,-99=-98") and may be named in at most one rule. Only plain NA cells are affected; tagged missing values are never converted by an NA rule. Under Stata or SAS convention the target may itself be a marker: "NA=.a".

Under Stata or SAS convention, values can be mapped to Stata-style or SAS-style missing values by their markers: "-99=.a; -98=.b".

A lettered marker can also be an OLD value, recoding the cells that carry it (either case: .a also names a SAS-style .A): ".a=-99" gives them a number, ".a=NA" makes them plain NA, and ".e=.c" re-letters them, merging them into .c if the variable already has it (a marker on the right needs Stata or SAS convention, as always). Markers and numbers may share a rule ("9,.a=-99"). A marker the map does not name is kept, with its label, whatever the else setting.

The right-hand side may also be the word missing (case-insensitive): the value is converted to your working convention's own missing form – a tagged marker under the Stata or SAS convention, or the first code from the missing.convention.codes setting in joptions under the SPSS convention, declared on the result automatically. The same map string therefore works under every setting: "8=missing; else=copy". The NA rule composes with it ("NA=missing" converts plain NA cells the same way), and labels = "missing=Refused" labels whatever the keyword produced. If the column already carries the other convention's markers while your missing.convention setting is set, the call stops and shows both resolutions rather than guessing.

Under the SPSS convention the code the keyword uses must be free: if the column already declares it, discretely or inside a declared range, the existing declaration is reused rather than added to; if it is an ordinary value in the column that the map leaves in place, or a value the map itself assigns, the call stops rather than sweep those cases into missingness; and if the declared codes the result keeps already fill what SPSS allows (three codes, or a range plus one code), the call stops and offers a declared code instead. Under the Stata or SAS convention the keyword always uses the first marker (.a, or .A), so a map that also assigns that marker itself stops rather than merge two missing categories onto one marker; the message offers the next free marker, or the same marker for both when that is what you mean. When the variable already carries that marker – data from Stata often has its own .a – the recoded cases join it, and a note says so and gives the line that keeps them apart (the next free marker). Under every convention, a labels = "missing=..." that differs from the label the marker or code already has stops, since it would rename the cases already there: the message gives the line that keeps the two apart and the line that merges them on purpose, keeping the existing label.

Examples:

  • "1=1; 2=0"

  • "1=1; 2,3=2; 4,5=3; else=NA"

  • "1=1; 2=0; else=copy"

  • "-5=System; else=copy"

  • "NA=-98; else=copy"

  • "3=1; 4=2; else=.a" (Stata or SAS convention only)

  • ".a=-99; .b=-98; else=copy" (a Stata-style variable to numeric codes, declared on the result)

  • "8=missing; else=copy" (any convention)

labels

Optional. A quoted string specifying value labels for the new variable, using the format "code=Label Text" with rules separated by semicolons. If supplied, these labels are used for the codes they name; the labels of declared missing values the result keeps (see Details) ride alongside them unless the string labels the same code. A label may be written in quotes, with either mark, right after its equals sign, and a semicolon inside the quotes is then part of the label: "1=Yes; 9=\"Refused; not asked\"". An unquoted label reads as it is written.

The left side of each rule may be a numeric code or, under Stata or SAS convention, a missing-value marker (.a through .z, or .A through .Z). A marker's label is stored on the marker itself, not on a numeric code. It may also be the word missing, labelling whatever the map's missing target produced: labels = "missing=Refused".

If omitted, the function attempts to transfer value labels automatically from the original variable. This works when the original variable has value labels and the mapping is one-to-one (no categories are collapsed). When categories are collapsed, labels cannot be transferred automatically and a note is printed.

Example: "1=Male; 0=Female" or ".a=Refused; .b=Don't know".

convention

Optional. One of "spss", "stata", "sas", or NULL (default); any capitalization is accepted. Controls whether missing-value markers (.a through .z or .A through .Z) are accepted in the map and labels arguments. Marker letters are matched case-insensitively; the stored markers take the convention's letter case (lowercase Stata-style under "stata", uppercase SAS-style under "sas"). Also decides what the missing keyword produces. Inert when neither a marker nor missing appears in either argument.

...

Not used. An input jrecode() does not take – a misspelled argument – stops with "unused input" and the name as typed, in place of R's own "unused argument".

When NULL, the convention is resolved from the missing.convention setting in joptions; if that is also unset, the call stops with a guided error asking you to choose – the package never infers a convention. Most users set the convention once at the top of a session via joptions() (or in their .Rprofile) rather than supplying this argument on every call. See ?joptions for details.

Value

A haven_labelled vector with the recoded values, variable label, and (if supplied or auto-transferred) value labels applied. The function returns the values rather than changing data: assign the result to a column to keep it. Two assignment forms do that job:

  • Preserving (recommended): MyData$AgeGroupR <- jrecode(MyData, AgeGroup, map = "...") adds a new column and keeps the original, so the two can be compared with jfreq() before the original is retired.

  • Overwrite: MyData$AgeGroup <- jrecode(MyData, AgeGroup, map = "...") replaces the variable in place. Nothing is wrong with this once the recode is verified, but the original values are gone.

SPSS users will recognize the pair: RECODE ... INTO new. is the preserving form and bare RECODE the overwrite.

Details

The function accepts haven-labelled, plain numeric, and logical variables. Factor, character, and date/time variables are refused with a message naming the fix — recoding works with a variable's numeric values, so convert these to numeric first.

The variable label from the original variable is carried across automatically with "(recoded)" appended. If the original variable has no variable label, the variable name is used instead.

Value labels are handled in three ways, in order of priority:

  1. If labels is supplied, those labels are used as-is.

  2. If labels is omitted and the original variable has value labels, they are automatically transferred to the new codes — provided the mapping is one-to-one (no collapsing). For example, recoding 1/2 to 1/0 will carry "Yes" and "No" across to the new codes automatically.

  3. If categories are collapsed (multiple old values map to one new value), automatic transfer is not possible and a note is printed directing you to supply labels manually. Recoding onto a value that else=copy also carries across ("5=4; else=copy") collapses two categories the same way.

Labels move with lettered markers as they do with numbers: a marker recoded to a number or to another marker takes its label along, and a value recoded to a marker gives the marker its label. Where a label would land on a value that keeps a label of its own ("-99=-98", or ".b=.a" on a variable with both markers), the value keeps its own; several different labels landing on one new value are all dropped. Labels that move onto a declared missing value or a marker also ride beside a supplied labels string, as the labels of kept declared values do.

NA values in the original variable are carried across as NA unless the map names NA as an old value (for example "NA=-98"); the else setting never converts NA. An NA rule affects plain NA cells only — tagged missing values (Stata-style or SAS-style) are declared missings and are preserved with their tags unless the map names them as old values (".a=-99"). Declared SPSS-style missing values on the original variable are likewise preserved, whether declared as discrete codes or as a range: a declared code the map does not name, and every value inside a declared range, are carried onto the result with the declaration and their labels under every else setting, and a note names the values the data hold (map them to NA explicitly to convert them instead). A declared code the map names is recoded like any other value; a range always carries whole, so a value the map recodes into the range is missing on the result.

A declared missing value the map moves onto a new code stays missing when the new code looks like a coded missing value (the check jload describes under Coded missing values): "-99=-88; else=copy" on a variable declaring -99 declares -88 on the result, with the label of -99 and a note saying so, and -99 is no longer declared. A lettered marker recoded to a number works the same way (".a=-99"). A new code that does not pass the check becomes an ordinary value – deliberately so for "-99=6", which makes refusals a category, but also for a negative code on a variable with large values, such as incomes, where the check finds nothing unusual; declare such a code with jdeclare_missing(). So does a new code that also receives ordinary values. A result that would hold both such a declared code and a lettered marker, or more declared codes than SPSS allows, stops the call with a message.

A variable holds one convention. When the original variable's SPSS-style declaration survives the map (a range always does; a discrete code does unless the map names it), Stata-style or SAS-style markers in the map or labels are refused with a pointer to jconvert(). A map that names every declared code ("-99=.a; -98=.b; else=copy" under Stata convention) leaves nothing SPSS-style behind and is the one-column migration pattern.

Values that merely look like coded missing values (e.g. -99, -9, 999) but are not declared are never changed on their own. Left unmapped with no else clause, they are named in the error that stops the call, with a nudge toward jdeclare_missing(); carried across by else=copy, they draw an advisory note at the full joutput tier. Recoding values to a code that looks like a missing value draws a note suggesting the declaration.

If the map does not include an else clause and there are unmapped values in the variable, the function stops with a message listing the unmapped values so you can fix the map before proceeding.

If the map specifies values that do not exist in the original variable, nothing is recoded for them and an advisory note says so at the full joutput tier. This helps catch typos in the map string.

Missing values in the map. The package supports three conventions for representing declared missing values, and the syntax for producing them from jrecode() depends on which one is active. A convention becomes active via joptions(missing.convention = ...) or this call's convention argument; with neither set, a call that produces declared missing values stops with a guided error asking you to choose.

Under SPSS convention, declared missing values are real numeric codes carrying metadata that flags them as missing. The two-step canonical pattern is:


df$EducR <- jrecode(df, Education,
                    map    = "1,2=1; 3=2; 4,5=3; -99,-98=-99",
                    labels = "1=High school or less; 2=Some college; 3=Degree")
jdeclare_missing(df, EducR, codes = c(Refused = -99), modify = TRUE)

The jrecode() call assigns the numeric code -99; the subsequent jdeclare_missing() call attaches the label and flags -99 as missing. Labeling -99 inside the labels argument is unnecessary — jdeclare_missing() owns that label.

The same two-step pattern serves data whose missingness arrived as plain NA (data born in R, or read from a CSV): map = "NA=-98; else=copy" converts the NA cells to the numeric code, and jdeclare_missing() declares it.

Missingness that arrived as Stata-style or SAS-style markers takes one step: naming the markers moves them to codes that are declared on the result (map = ".a=-99; .b=-98; else=copy"), as described above. jconvert(to = "spss") does the same for every such variable in a data frame, taking the codes from your setting.

Under Stata convention, declared missing values are typed missing cells marked with Stata-style tags (.a through .z). The single-call canonical pattern names the same codes:


df$EducR <- jrecode(df, Education,
                    map    = "1,2=1; 3=2; 4,5=3; -99,-98=.a",
                    labels = "1=High school or less; 2=Some college; 3=Degree; .a=Refused")

Under Stata convention, jdeclare_missing() is not needed for this pattern — jrecode() handles both the value recoding and the Stata-style missing-value labeling in one call. Naming the declared codes is what makes the call legal on a column that carries SPSS-style missing values: a map that left -99 and -98 in place (else=.a alone) would put a marker beside them and is refused.

SAS convention works the same way with SAS-style missing values (.A through .Z). Markers in the map and labels are matched case-insensitively, and the stored markers take the active convention's letter case: lowercase under Stata convention, uppercase under SAS convention.

Writing these markers while the active convention is SPSS raises an error naming the mismatch and the two ways out: restate the markers as numeric codes to stay in SPSS convention, or switch convention with joptions(missing.convention = ...) (or with this call's convention argument). The error does not rewrite the call for you: the SPSS-style codes would have to be taken from the missing.convention.codes setting in joptions, which cannot be known to be free of collision with values already in the column. The two-call SPSS-style pattern is documented above.

See also

jdeclare_missing for declaring missing values on a column after a recode (the SPSS-style canonical pattern).

jrelabel for applying labels to an existing variable after a recode.

joptions for the session-level missing.convention setting.

jstats for the package overview, workflow conventions, and complete function listing.

Examples

# Recode with explicit labels (a 1/2 dichotomy to 0/1)
df <- community
df$OwnsHome01 <- jrecode(df, OwnsHome,
                         map    = "1=1; 2=0",
                         labels = "0=No; 1=Yes")
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.

# The data frame may also be given by name
df$OwnsHome01 <- jrecode(data = df, OwnsHome, map = "1=1; 2=0",
                         labels = "0=No; 1=Yes")
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.

# Collapse categories (must supply labels)
df$RegionR <- jrecode(df, Region,
                      map    = "1,2=1; 3,4=2",
                      labels = "1=North or South; 2=East or West")
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.

# Use else=copy to carry unspecified values across unchanged
df$EducR <- jrecode(df, Education,
                    map    = "5=4; else=copy",
                    labels = "4=Bachelor's degree or higher")
#> Note: -99 ["Refused"], -98 ["Don't know"] are declared missing values and were
#> kept on the recoded variable.
#> To convert them to plain NA instead, map them to NA (for example -99=NA).
#> 
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.

# Use else=NA to deliberately drop unspecified values to system NA
df$EducR2 <- jrecode(df, Education,
                     map    = "4=1; 5=1; else=NA",
                     labels = "1=College degree")
#> Note: -99 ["Refused"], -98 ["Don't know"] are declared missing values and were
#> kept on the recoded variable.
#> To convert them to plain NA instead, map them to NA (for example -99=NA).
#> 
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.

# Convert a specific coded missing value to system NA
df$EducR3 <- jrecode(df, Education, map = "-99=System; else=copy")
#> Note: -98 ["Don't know"] is a declared missing value and was kept on the
#> recoded variable.
#> To convert it to a plain NA instead, add -98=NA to the map.
#> 
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.

# Give plain NA cells a codable value, then declare it. Declaring on a
# plain column needs a chosen missing-value convention (the package
# never infers one), so choose it first:
joptions(missing.convention = "spss")
#> Options Settings
#> Missing-value convention: SPSS-style
#> SPSS-style missing value codes: -99, -98, -97
#> Run joptions() to see all settings.
#> 
df$AgeR <- jrecode(df, Age, map = "NA=-98; else=copy")
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.
df <- jdeclare_missing(df, AgeR, codes = c("Not recorded" = -98))
#> Declared SPSS-style missing values on AgeR:
#>   -98 ["Not recorded"] (not present in the data)
#> 
#> This call changes df only if you assign the result:
#>   df <- jdeclare_missing(df, AgeR, ...)
#> 
#> To change df directly, rerun with modify = TRUE:
#>   jdeclare_missing(df, AgeR, ..., modify = TRUE)

# Stata convention: Stata-style missing values in map and labels, named
# by their markers (single call; convention = "stata" scopes the choice
# to this call only).
# Education carries SPSS-style missing values (-99, -98); naming them in
# the map moves them to the marker in the same call.
df$EducR4 <- jrecode(df, Education,
                     map    = "1,2=1; 3,4,5=2; -99,-98=.a",
                     labels = "1=No college; 2=College; .a=Refused",
                     convention = "stata")
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.

# SAS convention: the same single-call pattern; markers are matched
# case-insensitively and store in uppercase (.A)
df$EducR5 <- jrecode(df, Education,
                     map    = "1,2=1; 3,4,5=2; -99,-98=.a",
                     labels = "1=No college; 2=College; .a=Refused",
                     convention = "sas")
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.

# Using juse() default
juse(df)
#> Default data frame set to: df
df$RegionR2 <- jrecode(Region, map = "1,2=1; 3,4=2",
                       labels = "1=North or South; 2=East or West")
#> Note: This call changes df only if you assign the result:
#>   df$<name> <- jrecode(...)
#> To check the recode landed correctly, compare jfreq() on the original and the
#> new column.