install.packages("ksformat")
# From CRAN install.packages("ksformat") # From GitHub (dev) remotes::install_github("crow16384/ksformat") library(ksformat)
fnew( "M" = "Male", "F" = "Female", .missing = "Unknown", .other = "Other Gender", name = "sex" )
finput( "Male" = 1, "Female" = 2, name = "sex_inv" )
fnew_bid( "A" = "Active", "I" = "Inactive", name = "status" )
Creates both:
"status" VALUE format
"status_inv" INVALUE
fnew_date("DATE9.", name = "bday") fnew_date("%d.%m.%Y", name = "ru", type = "date") fnew_date("MMDDYY10.", name = "us", .missing = "NO DATE")
SAS names auto-resolved; R patterns also supported
| Param | Default | What it does |
|---|---|---|
name | NULL | Register in format library |
type | "auto" | "numeric"/"character" key type |
multilabel | FALSE | Allow multiple labels per value |
ignore_case | FALSE | Case-insensitive matching |
.missing | — | Label for NA/NaN/"" |
.other | — | Fallback for unmatched values |
fput( c("M", "F", "M", NA, "X"), "sex" )
fputn(c(5, 30, 70), "age") fputc(c("M", "F"), "sex")
fput_df(df, sex = format_get("sex"), age = format_get("age"), suffix = "_lbl" )
| sex | age | sex_lbl | age_lbl |
|---|---|---|---|
| M | 15 | Male | Child |
| F | 25 | Female | Adult |
| NA | 35 | Unknown | Adult |
fnew( "M" = "Male", "F" = "Female", name = "sex_nc", ignore_case = TRUE ) fput(c("m", "F", "M"), "sex_nc")
"m" matches "M" with ignore_case
| # | Condition | Result |
|---|---|---|
| ① | NA / NaN / "" | .missing label |
| ② | Exact match | Mapped label |
| ③ | Range match | Range label |
| ④ | No match | .other / original |
is_missing(NA) # TRUE is_missing(NaN) # TRUE is_missing("") # TRUE is_missing("x") # FALSE
fput(c("M", NA), "sex") fput(c("M", NA), "sex", keep_na = TRUE)
fparse(text = ' VALUE age (numeric) [0, 18) = "Child" [18, 65) = "Adult" [65, HIGH] = "Senior" .missing = "Age Unknown" ;') fputn(c(5,18,65,NA), "age")
| Syntax | Meaning | Math |
|---|---|---|
[a, b] | both inclusive | a ≤ x ≤ b |
[a, b) | right exclusive | a ≤ x < b |
(a, b] | left exclusive | a < x ≤ b |
(a, b) | both exclusive | a < x < b |
HIGH | +∞ upper bound | |
LOW | −∞ lower bound | |
fparse(text = ' VALUE bmi (numeric) [0, 18.5) = "Underweight" [18.5, 25) = "Normal" [25, 30) = "Overweight" [30, HIGH] = "Obese" .missing = "No data" ;')
| bmi | result |
|---|---|
| 16.2 | Underweight |
| 22.7 | Normal |
| 25.0 | Overweight |
| 35.1 | Obese |
| NA | No data |
fparse(text = ' VALUE score (numeric) (0, 50] = "Low" (50, 100] = "High" .other = "Out of range" ;')
| score | label |
|---|---|
| 0 | Out of range |
| 50 | Low |
| 51 | High |
| 101 | Out of range |
fparse(text = ' VALUE race (character) "W"="White" "B"="Black" "A"="Asian" .missing = "Unknown" ; INVALUE race_inv "White"=1 "Black"=2 "Asian"=3 ;')
Registers both in library:
range_spec(0, 18) range_spec(18, Inf, inc_high = FALSE)
finputn( c("Male", "Female", "Unknown"), "sex_inv" )
finputc( c("Active", "Pending"), "status_inv" )
"A""A"fputn(Sys.Date(), "DATE9.") fputn(Sys.Date(), "MMDDYY10.") fputn(Sys.Date(), "YYMMDD10.") fputn(Sys.Date(), "WORDDATE.") fputn(Sys.Date(), "QTR.")
| format | → result |
|---|---|
| DATE9. | 18MAR2026 |
| MMDDYY10. | 03/18/2026 |
| YYMMDD10. | 2026-03-18 |
| DDMMYY10. | 18/03/2026 |
| MONYY7. | MAR2026 |
| WORDDATE. | March 18, 2026 |
| YEAR4. | 2026 |
| QTR. | 1 |
days <- as.numeric( as.Date("2025-01-01") ) fputn(days, "DATE9.") fputn(days, "MMDDYY10.")
days = 20089
| secs | TIME8. | TIME5. | HHMM. |
|---|---|---|---|
| 0 | 0:00:00 | 0:00 | 00:00 |
| 3600 | 1:00:00 | 1:00 | 01:00 |
| 45000 | 12:30:00 | 12:30 | 12:30 |
| 86399 | 23:59:59 | 23:59 | 23:59 |
fputn(Sys.time(), "DATETIME20.")
| format | → result |
|---|---|
| DATETIME20. | 18MAR2026:13:48:58 |
| DATETIME13. | 18MAR26:13:48 |
| DTDATE. | 18MAR2026 |
| DTYYMMDD. | 2026-03-18 |
fnew_date("%d.%m.%Y", name = "ru_date") fput(birthdays, "ru_date")
v <- fnew_date("DATE9.", name = "vfmt", .missing = "NOT RECORDED") fput_df(patients, visit_date = v)
| id | visit_date | visit_fmt |
|---|---|---|
| 1 | 2025-01-10 | 10JAN2025 |
| 2 | 2025-02-15 | 15FEB2025 |
| 3 | <NA> | NOT RECORDED |
fnew( "1" = "date9.", "2" = "mmddyy10.", name = "wfmt", type = "numeric") datefmt <- fputn(key, "wfmt") date <- fputn(number, datefmt)
| num | key | fmt | date |
|---|---|---|---|
| 12103 | 1 | date9. | 03FEB2003 |
| 10899 | 2 | mmddyy10. | 11/07/1999 |
fparse(text = ' VALUE enrldt (date) pattern = "DATE9." .missing = "Not Enrolled" ; VALUE stamp (datetime) pattern = "DATETIME20." ;')
fprint() # list all fprint("sex") # detail
fmt <- format_get("sex") fclear("sex") # remove one fclear() # clear all
Retrieve format object
Remove single format
Clear entire library
cat(fexport(bmi = bmi_fmt))
x <- fimport("cntlout.csv") m <- fimport(csv, register = FALSE) fput(v, m[["GENDER"]])
fnew( "0,5,T,T" = "Infant", "6,11,T,T" = "Child", "12,17,T,T" = "Adolescent", "0,17,T,T" = "Pediatric", "18,64,T,T" = "Adult", "65,Inf,T,T" = "Elderly", "18,Inf,T,T" = "Non-Pediatric", name = "age_cat", type = "numeric", multilabel = TRUE ) fput_all( c(3, 14, 25, 70), "age_cat" )
fnew( "1,1,T,T" = "Mild", "2,2,T,T" = "Moderate", "3,3,T,T" = "Severe", "4,4,T,T" = "Life-threat.", "5,5,T,T" = "Fatal", "3,5,T,T" = "Serious", "1,2,T,T" = "Non-serious", name = "ae", type = "numeric", multilabel = TRUE )
stat <- fnew( "n" = "sprintf('%s', .x1)", "pct" = "sprintf('%.1f%%', .x1 * 100)", name = "stat" ) fput( c("n", "pct", "n", "pct"), stat, c(42, .053, 100, .255) )
r <- fnew( "ratio" = "sprintf('%s/%s', .x1, .x2)" ) fput("ratio", r, 3, 10) lbl <- fnew( "val" = "sprintf('%s(N=%s)', .x1, .x2)" ) fput(c("val", "val"), lbl, c(42, 55), 100)
.x2=100 recycled for all elements
sign <- fnew( "val" = "ifelse(.x1 > 0, paste0('+', .x1), as.character(.x1))" ) fput(rep("val", 3), sign, c(5, 0, -3)) # Mixed: static + expression fnew( "ok" = "OK", .other = "sprintf('Err(%s)', .x1)" )
"ok" → static · "E01" → .other expression
fnew( "1" = "groupx", "2" = "groupy", "3" = "groupz", name = "typefmt", type = "numeric" ) # define groupx, y, z... respfmt <- fput(type, "typefmt") word <- fputc(response, respfmt)
| type | resp | fmt | word |
|---|---|---|---|
| 1 | positive | groupx | agree |
| 1 | negative | groupx | disagree |
| 2 | positive | groupy | accept |
| 2 | negative | groupy | reject |
| 3 | positive | groupz | pass |
| 3 | negative | groupz | fail |
| Category | Function | Purpose |
|---|---|---|
| Create | fnew(..., name) | Discrete value→label |
finput(..., name) | Reverse label→value | |
fnew_bid(..., name) | Both format + invalue | |
fnew_date(pattern, name) | Date/time/datetime | |
| Apply | fput(x, format, ...) | Generic apply |
fputn(x, name, ...) | Numeric apply | |
fputc(x, name, ...) | Character apply | |
fput_all(x, format) | All labels (multilabel) | |
fput_df(data, ...) | Data frame columns | |
| Reverse | finputn(x, name) | Labels → numeric |
finputc(x, name) | Labels → character | |
| Library | format_get(name) | Get from library |
fprint(name) | List/inspect | |
fclear(name) | Remove format(s) | |
fimport(file) | SAS CNTLOUT CSV | |
| Utils | fparse(text, file) | Parse definitions |
fexport(..., file) | Export to text | |
range_spec(lo, hi) | Build range key | |
| Check | is_missing(x) | NA/NaN/"" → TRUE |
install.packages("ksformat") · github.com/crow16384/ksformat