--- title: "Smart features" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Smart features} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") library(ksCompare) ``` `ksCompare` adds several conveniences on top of plain `PROC COMPARE`-style diffing. ## ULP tolerance `ks_tol(ulp = N)` accepts pairs whose IEEE-754 binary64 representations are within `N` units in the last place. ```{r} x <- 1.0 y <- x + .Machine$double.eps ks_compare( data.frame(id = 1, x = x), data.frame(id = 1, x = y), by = "id", tolerance = ks_tol(ulp = 4) ) ``` ## Auto-key inference ```{r} df <- data.frame(study = "S1", id = 1:3, x = 1:3) suppressMessages(ks_compare(df, df, by = "auto")) ``` `by = "auto"` searches for the smallest combination of shared columns whose values are unique on both sides. ## Diff triage helpers `ks_cause_summary()` groups value diffs by likely cause, while `ks_row_diff_summary()` shows which matched observations changed the most. ```{r} a <- data.frame(id = 1:4, x = c("A", "b ", "c", "d"), y = c(1, 2, 3, 4)) b <- data.frame(id = 1:4, x = c("a", "b", "c", "d"), y = c(1, 9, 3, 9)) cmp <- ks_compare(a, b, by = "id") ks_cause_summary(cmp) ks_row_diff_summary(cmp) ``` ## First / last unequal observations For SAS-style review tables, `ks_compare()` also stores the first and last differing observations per matched column. ```{r} ks_compare(a, b, by = "id", n_first_last = 1)$first_last_unequal ``` ## Duplicate-key strategies ```{r} a <- data.frame(id = c(1, 1, 2), x = c(10, 99, 20)) b <- data.frame(id = c(1, 2), x = c(10, 20)) ks_compare(a, b, by = "id", dup_keys = "first") ks_compare(a, b, by = "id", dup_keys = "last") ks_compare(a, b, by = "id", dup_keys = "all_pairs") ``` `dup_keys = "keep_all"` pairs duplicate rows positionally within each key group (1<->1, 2<->2, ...). Leftover rows on the longer side become base- or compare-only, and `cmp$row_diff` carries a `pair_rank` / `pair_total` column so you can tell which pair each diff belongs to. The HTML report shows a *Pair* column when this strategy is active. ## SAS format normalisation Format comparison is trailing-dot- and case-tolerant, so `haven` import quirks do not surface as false schema diffs: ```{r, eval = FALSE} a <- data.frame(d = as.Date("2024-01-01")) attr(a$d, "format.sas") <- "DATE9." b <- a attr(b$d, "format.sas") <- "date9" # no trailing dot, lowercase ks_compare(a, b)$schema_diff # format_match == TRUE ``` ## Encoding-safe string comparison Strings imported from mixed Latin1 / UTF-8 sources are converted to UTF-8 before equality testing, so the same character in two encodings does not register as a diff. Combine with `str_norm = "NFC"` for fully Unicode-normalised comparison. ```{r, eval = FALSE} ks_compare( base, comp, by = "id", options = ks_comp_options(str_norm = "NFC") ) ``` ## Pattern detection Pattern detection is opt-in because the detectors scan every column with at least one diff. When many cells differ in the same way (constant offset, sign flip, trim-only string differences, etc.), set `find_patterns = TRUE` to populate `cmp$pattern_summary`: ```{r} a <- data.frame(id = 1:5, x = c(1, 2, 3, 4, 5)) b <- data.frame(id = 1:5, x = c(2, 3, 4, 5, 6)) ks_compare(a, b, by = "id", find_patterns = TRUE)$pattern_summary ``` Common labels include `constant_offset`, `constant_scale`, `sign_flip`, `integer_round`, `trim_only`, `case_only`, `epoch_swap`, and `factor_recoded`, depending on the column type.