Skip to contents

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.

Usage

jconvert(
  data,
  ...,
  to = NULL,
  vars = NULL,
  missing.notice = TRUE,
  modify = FALSE
)

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 with vars.

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 in jconvert(d, "stata"), stops and shows the named form. When to is left out, jconvert() reads the missing.convention setting in joptions: if the slot is set to "spss", "stata", or "sas", to resolves 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 ... and vars are 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. FALSE suppresses the message. The notification itself does not consult joutput(), 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 the juse() 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. When FALSE (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_range on haven_labelled_spss), masks declared codes to NA and removes the attributes; value labels are preserved so the column can still round-trip through jsave() with original labeling. For columns carrying Stata-style missing values (tagged_na markers), makes every marker a plain NA, labelled or not. A string variable's declared missing values (SPSS declares them directly, as in MISSING VALUES MARITAL ('UNKNOWN')) become NA the same way. They cannot become Stata-style or SAS-style missing values, which exist only on numeric variables, so to = "stata" and to = "sas" stop and name such a variable; jencode turns 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.codes setting in joptions (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, .n and .r converts exactly as one carrying .a, .b and .c does. SAS-style (uppercase) tags are case-corrected to Stata-style (lowercase) before the numeric mapping – for round-trip purposes the package treats .A and .a as 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 first missing.convention.codes value 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 -99 through -95, declared as the range -99 to -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, .r return as .a, .b, .c) with the same labels, and codes that do not run from the largest absolute value down – a missing.convention.codes setting such as c(-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_values codes are sorted by absolute value descending (ties broken with more-negative-first), then mapped .a, .b, .c in 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. SPSS c(-1, 9) -> Stata .b, .a -> SPSS c(-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 the missing.convention.codes setting 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 discrete na_values codes 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–.z alphabet) 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.

See also

jload for the load-time strip alternative (preserve.declarations = FALSE); joptions for setting the default convention and convention codes session-wide.

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
} # }