NEWS
ksformat 0.8.1
Documentation refresh
- Added a vignette example showing how to use
franges() labels directly with
tidyr::complete() and how to extract unique labels from the aesev format
mappings without converting to factor().
Internal refactoring (no breaking changes)
R/format_parse.R has been split in two: text-to-object parsing stays in
format_parse.R (874 lines); object-to-text rendering and CSV CNTLOUT
import/export (fexport(), fimport(), .format_to_text(), etc.) moves to
the new R/format_serialize.R (755 lines).
- Three DRY internal helpers added to
utilities.R:
.is_eval_label(), .parse_range_key_by_type(), .format_range_interval().
Duplicate switch/if-chains across format_apply.R, format_create.R,
and format_library_app.R now delegate to these helpers.
- Range-table now carries a
discrete_numeric_possible flag computed at
format creation time. fput() / fput_all() use it to skip the
as.character() + match() discrete-key pass for numeric / Date / POSIXt
inputs against formats whose discrete keys are all non-numeric strings,
broadening the existing fast path.
- Friendly-interval regex in
.block_to_stratified_range_format() is now
built once per format parse instead of once per mapping entry.
ksformat 0.7.2
New features
finput() gains an ignore_case argument. When TRUE, label lookup is
case-insensitive (equivalent to SAS INVALUE name (nocase)). Default
FALSE preserves existing behaviour.
fparse() now correctly propagates the (nocase) option on INVALUE
blocks to the resulting ks_invalue object. Previously the flag was
parsed but silently discarded.
fexport() / fparse() round-trips now preserve ignore_case for
invalue objects: exporting a nocase invalue emits INVALUE name (nocase)
which re-imports with ignore_case = TRUE.
print.ks_invalue() now displays a (nocase) flag in the header when
ignore_case = TRUE.
ksformat 0.7.1 (2026-05-21)
New features
fputk() gains a na_as_string argument. When TRUE, an NA in any
key component is preserved as the literal string "NA" produced by
paste() instead of being restored to NA_character_. This enables
round-trip lookups against formats built via
fmap(paste(..., sep = "|"), values), where the stored keys
themselves contain "NA". Default FALSE preserves existing behavior
(NA → .missing / keep_na).
- New
finputk() — composite-label wrapper around INVALUE lookup,
mirroring fputk() on the reverse direction. Supports the same
sep and na_as_string arguments and dispatches on the stored
invalue's target_type.
Documentation
- Vignette Example 28: Composite Key Lookup with NA Components —
explains why
paste()-built keys contain the literal string "NA" for
NA inputs, shows the incorrect result with the default
na_as_string = FALSE, and demonstrates the correct round-trip with
na_as_string = TRUE using a clinical LB PARAMCD derivation scenario.
- Vignette Example 29: Composite Label Invalue Lookup with
finputk() —
basic two-column reverse lookup and na_as_string = TRUE usage with
NA key components.
examples/CompositeKeyNA.R — runnable companion script for both examples.
ksformat 0.7.0
New features
- New format type
stratified_range — combines a discrete stratum
(study arm, subject id, composite key, …) with per-stratum numeric /
Date / POSIXct range buckets. Apply with fputk(), passing the
stratum column(s) first and the value column last.
- New builder
fmap_strata(stratum, low, high, label, inc_low, inc_high, sep) — produces a ks_fmap ready for
fnew(..., type = "stratified_range"). The chosen separator is
carried as an attribute and picked up automatically by fnew().
- New builder
fmap_ranges(low, high, label, inc_low, inc_high) —
convenience helper that turns parallel vectors of numeric / Date /
POSIXct bounds and labels into canonical range keys, removing the
need to hand-craft "low,high,inc_low,inc_high" strings.
fparse() / fexport() support the new type via the
VALUE name (stratified_range, range_subtype: numeric|date|datetime, strata_sep: |) block syntax. Both canonical
"STRATUM|low,high,inc_low,inc_high" keys and the friendly
"STRATUM"|[low, high) interval form are accepted, including
per-stratum .missing|STRATUM / .other|STRATUM directives.
print.ks_format() renders stratified formats grouped under
Stratum "X": headers.
Documentation
- Vignette Example 26: Stratified Range Lookup with
fputk() —
programmatic and text-based construction, date subtype with
per-subject windows, per-stratum and global .other fallbacks, and
fexport()/fparse() roundtrip.
- Vignette Example 27: Plain Range Lookup with
fmap_ranges() —
builds a numeric age-band format without hand-crafted canonical keys.
examples/StratifiedRanges.R — runnable companion script.
ksformat 0.6.7
Documentation
- Added vignette Example 25: Date Range Bucketing covering
date_range and
datetime_range types: fiscal-year bucketing, fparse() with ISO bounds,
LOW/HIGH open-ended arms, fput_all() multilabel overlapping windows,
auto-detection, fexport()/fparse() roundtrip, and datetime_range
shift bucketing.
- Added
examples/DateRanges.R — a self-contained runnable script with the
same scenarios for quick interactive exploration.
ksformat 0.6.6
New features
date_range and datetime_range format types: bucket Date or POSIXct
values into character labels using interval bounds written as ISO
date/datetime strings. Both types reuse the numeric range-table fast
path (sorted-disjoint ranges hit findInterval() in C).
fnew() accepts type = "date_range" / "datetime_range" and a new
optional date_format argument for parsing custom bound strings.
fparse() accepts interval notation with date/datetime bounds, e.g.
[2024-01-01, 2025-01-01) = "FY24" and
[2024-01-01 08:00, 2024-01-01 16:00) = "Day". The type is auto-detected
when no explicit subtype is given.
fexport() renders date/datetime range bounds as ISO strings, enabling
full roundtrip through fparse().
- Bounds support
LOW / HIGH keywords (rendered as -Inf / +Inf and
emitted back as LOW / HIGH on export) and exclusive ( / )
brackets.
ksformat 0.6.5
Performance
- Precomputed range table:
ks_format objects now carry a pre-built
range_table field. Range keys are parsed once at format-creation time
(in fnew(), fparse(), fimport()) rather than on every fput() call.
findInterval() fast path in fput(): sorted, non-overlapping numeric
ranges with standard [low, high) semantics now use R's built-in
findInterval() (O(n log k) in C), giving a ~10–14× speedup over the
previous per-range R loop on large inputs (benchmarked at 1M rows).
skip_discrete optimisation in fput(): pure numeric-range formats
(no discrete keys) with numeric input now skip the as.character() +
match() step entirely.
is_missing(): removed a redundant is.nan() pass on numeric vectors —
is.na() already returns TRUE for NaN.
Bug Fixes
.build_range_table(): ranges defined out-of-order are now sorted by
(low, high) before storage, so the findInterval fast path triggers
regardless of definition order.
.build_range_table(): removed a dead attr<- loop that silently had
no effect (subsetting a character vector creates a copy, stripping any
attribute set on the subset).
ksformat 0.6.4
New Features
- Added
franges(): extracts all range entries from a ks_format object as a
tidy data.frame with columns low, high, inc_low, inc_high, and
label. Accepts either a ks_format object or a registered format name.
- Added
fmap_to_ranges(): reverse-looks up range bounds by label — given a
vector of coded values that match range labels in a format, returns the
corresponding low / high bounds (and inclusivity flags) per element.
ksformat 0.6.3
Bug Fixes
- Fixed
fput() (and fputk()) losing the tzone attribute when returning
POSIXct values from a value-type format. The result vector was seeded from
a tzone-less NA, so values were silently displayed in the local timezone
instead of the source timezone (e.g. UTC), causing an apparent time-shift.
The tzone attribute of the mapped values is now propagated to the result.
ksformat 0.6.2
New Features
- Added
format_library_app() Shiny browser for the global format library,
including filters by name/object type, per-object details, and mapping table
rendering for both ks_format and ks_invalue entries.
- Added an RStudio addin entry (Format Library Browser) to launch
format_library_app() from the Addins menu.
Enhancements
- Replaced plain-text print preview in the app with a formatted mapping table
that shows mappings, range rules, and special directives (
.missing,
.other) in structured form.
- Added app management UX improvements: explicit quit action with confirmation
modal, plus confirmations for destructive clear/remove operations.
Documentation
- Added
format_library_app() docs and package index references.
- Updated README with interactive browser usage and behavior summary.
ksformat 0.6.1
Documentation
- Cheat sheet updated: added
fputk() examples (basic composite key,
fmap() data-driven Date lookup, fparse() text-defined Date lookup),
added fmap() to the Function Reference table, and rearranged layout to
fit landscape page.
- Vignette Example 22 added: "Date Lookup via
fparse() and fputk()" —
character lookup, native Date lookup with fmap(), and round-trip via
fexport()/fparse().
ksformat 0.6.0
Breaking Changes
fnew() no longer accepts the reverse parameter. Use fmap(keys, values)
instead of setNames(values, keys) with reverse = FALSE to create
data-driven formats consistently for all types.
New Features
- New
fmap(keys, values) helper creates a key-value mapping that tells
fnew() to use the natural direction (keys → values) without reversal.
Works identically for character, numeric, Date, POSIXct, and logical formats.
Documentation
- Vignette Example 21 rewritten: "Consistent Data-Driven Formats with
fmap()" — demonstrates how fmap(keys, values) replaces the old
setNames() + reverse = FALSE pattern.
- Updated Example 20 (Composite Key Lookup) to use
fmap().
ksformat 0.5.1
Bug Fixes
- Fixed Example 20 (Composite Key Lookup) in vignette: added missing
reverse = FALSE to the character format created with setNames(), which
caused all lookups to return "NOT FOUND".
ksformat 0.5.0
New Features
fnew() gains a reverse parameter for explicit control over named-vector
direction. Set reverse = FALSE to use the natural setNames(values, keys)
convention consistently for all format types — character, numeric, Date,
POSIXct, and logical. The default (NULL) preserves backward-compatible
auto-detection: reversal for character/numeric, no reversal for value types.
Documentation
- New vignette Example 21: "Consistent Data-Driven Formats with
reverse" —
demonstrates how to build lookup formats from data frames using the same
setNames() pattern for all output types.
- Updated
fnew() documentation with detailed explanation of the named-vector
reversal convention and the new reverse parameter.