Compares two data frames (or tibbles) at the dataset, variable, and observation levels,
providing an audit-ready, tidy alternative to SAS PROC COMPARE and legacy comparison
utilities (such as arsenal::comparedf). Evaluates variable presence, data types,
variable labels (from labelled), row counts, and value-level discrepancies within
a user-specified numerical tolerance. It agrees with PROC COMPARE on the benchmark
described under “Agreement with SAS PROC COMPARE”, and differs from it in the ways
listed there.
Usage
compare_df(base, compare, ...)
# Default S3 method
compare_df(
base,
compare,
by = NULL,
tolerance = 1e-07,
base_name = NULL,
compare_name = NULL,
max_diffs = 100,
...
)
# S3 method for class 'cbe_database'
compare_df(
base,
compare,
by = NULL,
edge_by = c("from", "to"),
...,
base_name = NULL,
compare_name = NULL
)
# S3 method for class 'igraph'
compare_df(base, compare, ...)
# S3 method for class 'visNetwork'
compare_df(base, compare, ...)Arguments
- base
The base data frame or tibble (equivalent to SAS
base=).- compare
The comparison data frame or tibble (equivalent to SAS
compare=).- ...
Passed on to methods (and, for the
cbe_databasemethod, on to the per-tablecompare_df()calls – e.g.by/tolerance).- by
Optional character vector of column names specifying key variables used to align observations across datasets (equivalent to SAS
idorbystatement). IfNULL(default), observations are compared by row order. Rows that share a key are paired in order of appearance (with a warning); surplus duplicates on either side count as unmatched. A key column named like one of thediffscolumns (variable,label, ...) is prefixed withkey_there. For thecbe_databasemethod, this is the node key (passed through to the nodes comparison only).- tolerance
Non-negative numeric threshold for numeric differences (default:
1e-7). Differences with absolute magnitude less than or equal totoleranceare considered matches.- base_name
Optional character string identifying the base dataset. If
NULL, deparsed from thebaseargument.- compare_name
Optional character string identifying the comparison dataset. If
NULL, deparsed from thecompareargument.- max_diffs
Maximum number of discrepant rows to store per variable (default: 100).
- edge_by
cbe_databasemethod only: key column(s) used to align edges (defaultc("from", "to"), which everyas_databasemethod produces).
Value
An S3 object of class "cbe_compare_df" containing (or, for a
cbe_database/graph comparison, a "cbe_compare_database"
list of two such objects, named nodes and edges):
- meta
List containing metadata on dataset names, dimensions, tolerance, and keys.
- variables
List with elements
common,base_only, andcompare_only.- summary
Tibble summarizing comparison status for each variable (types, match status, difference counts, max difference, RMSE).
- observations
List summarizing observation matching (counts, keys matched, unmatched).
- diffs
Tibble containing detailed cell-by-cell discrepancies for all variables exceeding tolerance.
- is_concordant
Logical indicating whether the datasets match completely within tolerance on all common variables and observations.
Agreement with SAS PROC COMPARE
The package tests compare the results with SAS 9.4 PROC COMPARE
(METHOD=ABSOLUTE, CRITERION equal to tolerance, by as the
ID statement) on nine small comparisons typed into
inst/sas/benchmark_proc_compare.sas; its listing is committed under
inst/sas/list/. On that data both agree on the number of variables in common and
only in either data set; of observations matched and only in either data set (by key,
by position, and with repeated keys paired in order); of values that differ, per variable
and in all, and of observations with a difference; of values that are not exactly equal
(tolerance = 0); on the maximum difference; and on every cell that differs. A value
differs if abs(difference) > tolerance (a difference of exactly tolerance is
none), and a missing value against a value is a difference. The agreement is on the
variables SAS compares (see the second item below) and on that data; it is not a proof
for other inputs.
The two define some things differently:
The sign:
diffindiffsis base minus compare;PROC COMPAREprintsDiff.as compare minus base.A variable whose type differs between the data sets is still compared, as text, and reported with
types_match = FALSE;PROC COMPAREcompares nothing and counts a conflicting type.“Type” is the R class:
integeragainstnumeric, orfactoragainstcharacter, is a type mismatch, although SAS has only numeric and character variables.Strings are compared as they are: a trailing blank is a difference (SAS pads the shorter value), and
NAdiffers from""(SAS's missing character value is the blank).Only the type is checked, not the length, format, informat or label (SAS reports a character variable of different lengths as a differing attribute).
Only absolute differences.
METHOD=PERCENTandRELATIVE,BYgroup reports and special missing values are not part of the benchmark.compare_df()needs no sorted data;PROC COMPAREdoes (the benchmark sorts the COMPARE data set).
Examples
df1 <- data.frame(id = 1:5, x = c(1, 2, 3, 4, 5), y = c("a", "b", "c", "d", "e"))
df2 <- data.frame(id = 1:5, x = c(1, 2, 3.0001, 4, 5), y = c("a", "b", "c", "d", "f"))
cmp <- compare_df(df1, df2, by = "id", tolerance = 1e-3)
print(cmp)
#> ----------------------------------------------------------------------
#> TempleCBE Data Frame Comparison (modelled on SAS PROC COMPARE)
#> ----------------------------------------------------------------------
#> Base Data: df1 (N = 5, P = 3)
#> Compare Data: df2 (N = 5, P = 3)
#> By Variables: id
#> Tolerance: 0.001
#> ----------------------------------------------------------------------
#>
#> -- Variable Concordance ----------------------------------------------
#> Variables in Common: 3
#>
#> -- Observation Concordance -------------------------------------------
#> Matched Observations: 5
#>
#> -- Discrepancies Summary ---------------------------------------------
#> Variables with Differences: 1 / 2
#>
#> variable label type_base type_compare types_match n_diff max_diff rmse
#> y <NA> character character TRUE 1 NA NA
#>
#> Discrepant Values (showing up to 10 rows):
#> id row_base row_compare variable label base_value compare_value diff
#> 5 5 5 y <NA> e f NA
#> ----------------------------------------------------------------------
generics::tidy(cmp)
#> # A tibble: 1 × 8
#> id row_base row_compare variable label base_value compare_value diff
#> <int> <int> <int> <chr> <chr> <chr> <chr> <dbl>
#> 1 5 5 5 y NA e f NA
g1 <- igraph::graph_from_data_frame(
data.frame(from = c("a", "b"), to = c("b", "c")),
vertices = data.frame(name = c("a", "b", "c"), stage = c("eda", "eda", "report"))
)
g2 <- igraph::graph_from_data_frame(
data.frame(from = c("a", "b"), to = c("b", "c")),
vertices = data.frame(name = c("a", "b", "c"), stage = c("eda", "analysis", "report"))
)
compare_df(g1, g2, by = "name")
#> == Nodes ===============================================================
#> ----------------------------------------------------------------------
#> TempleCBE Data Frame Comparison (modelled on SAS PROC COMPARE)
#> ----------------------------------------------------------------------
#> Base Data: as_database(base)$nodes (N = 3, P = 2)
#> Compare Data: as_database(compare)$nodes (N = 3, P = 2)
#> By Variables: name
#> Tolerance: 1e-07
#> ----------------------------------------------------------------------
#>
#> -- Variable Concordance ----------------------------------------------
#> Variables in Common: 2
#>
#> -- Observation Concordance -------------------------------------------
#> Matched Observations: 3
#>
#> -- Discrepancies Summary ---------------------------------------------
#> Variables with Differences: 1 / 1
#>
#> variable label type_base type_compare types_match n_diff max_diff rmse
#> stage <NA> character character TRUE 1 NA NA
#>
#> Discrepant Values (showing up to 10 rows):
#> name row_base row_compare variable label base_value compare_value diff
#> b 2 2 stage <NA> eda analysis NA
#> ----------------------------------------------------------------------
#>
#> == Edges ===============================================================
#> ----------------------------------------------------------------------
#> TempleCBE Data Frame Comparison (modelled on SAS PROC COMPARE)
#> ----------------------------------------------------------------------
#> Base Data: as_database(base)$edges (N = 2, P = 2)
#> Compare Data: as_database(compare)$edges (N = 2, P = 2)
#> By Variables: from, to
#> Tolerance: 1e-07
#> ----------------------------------------------------------------------
#>
#> -- Variable Concordance ----------------------------------------------
#> Variables in Common: 2
#>
#> -- Observation Concordance -------------------------------------------
#> Matched Observations: 2
#>
#> -- Discrepancies Summary ---------------------------------------------
#> Result: All values match within tolerance 1e-07.
#> Status: Data sets are completely CONCORDANT.
#> ----------------------------------------------------------------------
