jconvert() provides a single entry point for changing how
declared missing values are represented on the columns of a data
frame already in memory. Four target formats are supported: SPSS-style
(na_values on haven_labelled_spss), Stata-style
(lowercase tagged_na on haven_labelled), SAS-style
(uppercase tagged_na on haven_labelled), and base R
(declarations stripped, declared cells converted to plain NA).
The haven package and its documentation call these user-defined
missing values.
Arguments
- data
A data frame, or omitted to use the
juse()default.- ...
Optional unquoted variable names, straight after the data frame, as in every data-first function:
jconvert(d, Income, Education, to = "stata"). When supplied, only the listed variables are scanned. Mutually exclusive withvars.- to
One of
"baseR","spss","stata", or"sas"(any capitalization is accepted), always named:to = "stata". A format name given where a variable goes, as injconvert(d, "stata"), stops and shows the named form. Whentois left out,jconvert()reads themissing.conventionsetting injoptions: if the slot is set to"spss","stata", or"sas",toresolves to that value; if the slot is at its"none"default,jconvert()errors with guidance naming the concrete options. The destructive"baseR"target is never auto-resolved – it must always be passed explicitly.- vars
Alternative scope-by-vector path: a character vector of variable names. Mutually exclusive with
.... When both...andvarsare empty,jconvert()operates on the whole data frame.- missing.notice
Logical;
TRUE(default) prints a notification summarizing what was converted – one row per missing value, with its label and what it became – and what was skipped, along with a reminder of how to keep the result.FALSEsuppresses the message. The notification itself does not consultjoutput(), because the function reports an action it just performed rather than explaining system behavior; only the keep-the-result reminder follows the tier, and is omitted at"minimal".- modify
Logical. When
TRUE, the converted data frame is written back onto the data frame named in the call (or onto thejuse()default when the data argument is omitted), so no assignment is needed – the recommended workflow, since conversions lost to a forgotten assignment silently change how later analyses treat the affected values. Requires the data frame to be given as a plain name. WhenFALSE(the default), the caller's data frame is untouched; assign the returned data frame to keep the conversions.
Value
The data frame with the requested conversions applied, returned
invisibly. With the default modify = FALSE, the caller's data
frame is unchanged until the result is assigned back. With
modify = TRUE, the conversions are also written back onto the
caller's data frame, and the returned copy can be ignored.
Details
The four target formats:
to = "baseR"Strip all missing-value declarations and convert declared cells to plain
NA. For SPSS-style columns (na_values/na_rangeonhaven_labelled_spss), masks declared codes toNAand removes the attributes; value labels are preserved so the column can still round-trip throughjsave()with original labeling. For columns carrying Stata-style missing values (tagged_namarkers), makes every marker a plainNA, labelled or not. A string variable's declared missing values (SPSS declares them directly, as inMISSING VALUES MARITAL ('UNKNOWN')) becomeNAthe same way. They cannot become Stata-style or SAS-style missing values, which exist only on numeric variables, soto = "stata"andto = "sas"stop and name such a variable;jencodeturns it into a numeric one.to = "spss"Convert Stata-style or SAS-style missing values to SPSS-style numeric codes. Each column's distinct letter tags, in letter order, take the
missing.convention.codessetting injoptions(default-99,-98,-97) in order: the lowest letter takes the first code, the next the second, and so on, whatever the letters are – a column carrying.d,.nand.rconverts exactly as one carrying.a,.band.cdoes. SAS-style (uppercase) tags are case-corrected to Stata-style (lowercase) before the numeric mapping – for round-trip purposes the package treats.Aand.aas the same conceptual marker, and mixed-case columns collapse to a single lowercase marker (SPSS has no parallel uppercase convention). The notification's per-column display shows the original (pre-correction) tag for SAS-corrected columns – e.g..A ["Refused"] -> -99– so the user-visible mapping reflects what was actually in the data on input. A marker declared only through a value label, with no case carrying it yet, converts like one that cases carry: its label moves to the code and the code is declared missing. A column with more distinct tags than the setting has codes is declared with a missing-value range instead, SPSS's own form for more than three: the codes start at the firstmissing.convention.codesvalue and run one per tag in the direction the codes run, from the first toward the second – with the defaults, toward zero, so five tags take-99through-95, declared as the range-99to-95, each tag's label on its code. The first tags keep their usual codes whenever the setting's codes are consecutive, and the notification shows the range and says why it was used. A setting with a single code (which gives no direction), or one whose range would reach zero, is refused before any data is touched, with the fix. Because the SPSS-to-Stata direction assigns letters by code order, a tag set other than the leading letters comes back as the leading letters (.d,.n,.rreturn as.a,.b,.c) with the same labels, and codes that do not run from the largest absolute value down – amissing.convention.codessetting such asc(-1, -2, -3)– come back with their letters in another order; the notification says so whenever a letter would change.to = "stata"Convert SPSS-style numeric codes to Stata-style missing values. Letter tags are assigned by ordering rather than by convention: each column's own declared
na_valuescodes are sorted by absolute value descending (ties broken with more-negative-first), then mapped.a, .b, .cin that order. Convention codes are NOT consulted for this direction; they only govern the reverse (Stata to SPSS) mapping. Round-trip conversions are not guaranteed to preserve the original numeric codes (e.g. SPSSc(-1, 9)-> Stata.b, .a-> SPSSc(-98, -99)loses the original numbers), but the value labels survive intact and the missingness semantics are preserved. The notification says so whenever the trip back under themissing.convention.codessetting would give other codes: "converting Score back to SPSS-style missing values would give its codes -99, -98, not 9, -1." Range-based SPSS missings (na_range) are enumerated: Stata-style missing values have no range concept, so the distinct range values present in the column's data, plus any range values carrying a value label, are translated individually. They join the column's discretena_valuescodes in a single set, sorted and lettered by the same ordering rule. The range rule itself is not preserved – a range value with neither a data occurrence nor a label at conversion time is not translated, so if it first appears in later data it arrives as an ordinary data value. A column whose combined set exceeds 26 values (the.a–.zalphabet) is refused before any data is touched. A range declaration with no values to translate does not block the conversion: the column still converts, with the empty range declaration dropped and reported. SAS-style (uppercase) tagged columns are case-corrected to Stata-style (lowercase) and counted as converted; columns already fully lowercase are skipped as already in the target form.to = "sas"Identical to
to = "stata"except that the letters are uppercase (.A–.Z, SAS's native extended-missing convention): SPSS-style columns are enumerated and mapped to uppercase tags, Stata-style (lowercase) tagged columns are case-corrected to uppercase and counted as converted, and columns already fully uppercase are skipped. Inside R the two tagged forms are the same structure differing only in letter case; note that saving to Stata format (.dta) lowercases uppercase tags again, since the .dta format only supports lowercase letters.
Pre-flight checks for to = "spss" include a collision check:
if a column's target numeric code (e.g. -99 for .a) is
present as genuine data in the column, the call errors before any
data is touched; for a column declared with a range, any value inside
the range counts. The check covers a marker declared only through a
value label as well as one that cases carry. The error message lists
every colliding column and the remedy: change the convention codes via
joptions(missing.convention.codes = ...). When the same call
also meets columns with more tags than codes, it adds the option of
scoping the call with vars = c(...) to leave the affected
columns out. Atomicity applies to every
error mode – the entire jconvert() call either succeeds or
errors before mutating the data frame.
Pattern A – value labels suggest missingness but no formal
declaration. When a numeric column has no formal missing-value
declaration but carries value labels matching the package's
missing-label wordlist (e.g. "Refused", "Don't know",
"Not applicable"), jconvert() skips the column and
surfaces it in the notification with the affected value/label pairs.
To formalize these as declared missing values use
jdeclare_missing(); to leave them as ordinary data, no action
is needed. A text variable's labelled values are reported under
to = "spss" and to = "baseR", and declared with their
values in quotes; not under "stata" or "sas", whose
missing values exist only on numeric variables.
Examples
# community ships with SPSS-style missing values (Income, Education,
# Smoker, Environment1, Environment3), so the conversions run on it
# directly.
# Convert SPSS-style to Stata-style missing values. modify = TRUE
# writes the result back onto df in one step -- the recommended
# workflow.
df <- community
jconvert(df, to = "stata", modify = TRUE)
#> Converted to Stata-style missing values in 5 variables:
#> Income -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#> Education -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#> Smoker -99 ["Refused"] -> .a
#> Environment1 -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#> Environment3 -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#>
#> To keep it across sessions, save the data frame:
#> jsave(df, "df.rds")
# Equivalent without modify: assign the returned data frame back
df2 <- jconvert(community, to = "stata")
#> Converted to Stata-style missing values in 5 variables:
#> Income -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#> Education -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#> Smoker -99 ["Refused"] -> .a
#> Environment1 -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#> Environment3 -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#>
#> This call changes community only if you assign the result:
#> community <- jconvert(community, ...)
#>
#> To change community directly, rerun with modify = TRUE:
#> jconvert(community, ..., modify = TRUE)
# Convert to SAS-style missing values (uppercase .A, .B, ...):
df_sas <- jconvert(community, to = "sas")
#> Converted to SAS-style missing values in 5 variables:
#> Income -99 ["Refused"] -> .A
#> -98 ["Don't know"] -> .B
#> Education -99 ["Refused"] -> .A
#> -98 ["Don't know"] -> .B
#> Smoker -99 ["Refused"] -> .A
#> Environment1 -99 ["Refused"] -> .A
#> -98 ["Don't know"] -> .B
#> Environment3 -99 ["Refused"] -> .A
#> -98 ["Don't know"] -> .B
#>
#> This call changes community only if you assign the result:
#> community <- jconvert(community, ...)
#>
#> To change community directly, rerun with modify = TRUE:
#> jconvert(community, ..., modify = TRUE)
# Strip the declarations from every applicable variable:
df3 <- jconvert(community, to = "baseR")
#> Stripped the missing-value declarations from 5 variables:
#> Income -99 ["Refused"]
#> -98 ["Don't know"]
#> Education -99 ["Refused"]
#> -98 ["Don't know"]
#> Smoker -99 ["Refused"]
#> Environment1 -99 ["Refused"]
#> -98 ["Don't know"]
#> Environment3 -99 ["Refused"]
#> -98 ["Don't know"]
#>
#> This call changes community only if you assign the result:
#> community <- jconvert(community, ...)
#>
#> To change community directly, rerun with modify = TRUE:
#> jconvert(community, ..., modify = TRUE)
# Some variables only: name them after the data frame, then the target
df4 <- jconvert(community, Income, Education, to = "baseR")
#> Stripped the missing-value declarations from 2 variables:
#> Income -99 ["Refused"]
#> -98 ["Don't know"]
#> Education -99 ["Refused"]
#> -98 ["Don't know"]
#>
#> This call changes community only if you assign the result:
#> community <- jconvert(community, ...)
#>
#> To change community directly, rerun with modify = TRUE:
#> jconvert(community, ..., modify = TRUE)
# The same, as a character vector:
df5 <- jconvert(community, vars = c("Income", "Education"), to = "baseR")
#> Stripped the missing-value declarations from 2 variables:
#> Income -99 ["Refused"]
#> -98 ["Don't know"]
#> Education -99 ["Refused"]
#> -98 ["Don't know"]
#>
#> This call changes community only if you assign the result:
#> community <- jconvert(community, ...)
#>
#> To change community directly, rerun with modify = TRUE:
#> jconvert(community, ..., modify = TRUE)
# Suppress the notification (e.g. inside a script):
df6 <- jconvert(community, to = "baseR", missing.notice = FALSE)
# The three ways to give the data frame
df7 <- jconvert(community, Income, to = "stata") # positional
#> Converted to Stata-style missing values in 1 variable:
#> Income -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#>
#> This call changes community only if you assign the result:
#> community <- jconvert(community, ...)
#>
#> To change community directly, rerun with modify = TRUE:
#> jconvert(community, ..., modify = TRUE)
df8 <- jconvert(data = community, Income, to = "stata") # named
#> Converted to Stata-style missing values in 1 variable:
#> Income -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#>
#> This call changes community only if you assign the result:
#> community <- jconvert(community, ...)
#>
#> To change community directly, rerun with modify = TRUE:
#> jconvert(community, ..., modify = TRUE)
juse(community) # juse() default
#> Default data frame set to: community
df9 <- jconvert(Income, to = "stata")
#> Converted to Stata-style missing values in 1 variable:
#> Income -99 ["Refused"] -> .a
#> -98 ["Don't know"] -> .b
#>
#> This call changes community only if you assign the result:
#> community <- jconvert(community, ...)
#>
#> To change community directly, rerun with modify = TRUE:
#> jconvert(community, ..., modify = TRUE)
juse(NULL)
#> Default data frame cleared.
if (FALSE) { # \dontrun{
# Convert with target inferred from joptions:
joptions(missing.convention = "spss")
df <- jconvert(df) # converts any Stata-style columns to SPSS
} # }