Repository navigation
Print mis-matched rows via the ARD print method; add NEWS and coverage - #3
Merged
ddsjoberg merged 5 commits intoSep 7, 2026
Conversation
Follow-up to the #605 fix. `compare_ard()` landed before ARDs printed through pillar/tibble (#594), so `print.compare_ard()` coerced with `as.data.frame()` to get scalar list-column values to show. `card_list_shaft()` does that now, and the frames are still `card` objects -- dplyr's default `dplyr_reconstruct` preserves the class through the `select()`/`anti_join()` in `.compare_rows()`. Dropping the coercion in the two mis-matched row blocks bounds the output (10 rows plus a `# i N more rows` footer) and renders `group1_level`/`variable_level` properly. The comparison block keeps `as.data.frame()` + `head(n = 10)`: its `difference` column holds `all.equal()` messages that pillar would truncate with `~` rather than wrap. Also adds the NEWS.md entries, a snapshot covering both mis-matched row blocks being non-empty at once, a snapshot showing a long block truncating, and an `expect_output()` assertion outside of any snapshot so that regenerating snapshots cannot quietly re-accept the missing output from #605. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
testthat pins `width` to 80, which splits the ~120-character comparison
tables into two column chunks and makes the snapshots hard to read. Setting
`withr::local_options(width = 200)` at test scope prints each table in one
piece. `expect_snapshot()` does not re-pin `width`, so the option survives.
`cli.width` is pinned separately by testthat, so the section rules stay 80
characters wide, and the ARD print method is unaffected -- the mis-matched
row blocks are byte-identical. Net effect is 34 fewer snapshot lines, since
unchunking drops the repeated row-index columns.
The option is deliberately test-only: `withr` is in Suggests, and in a real
session `cli` falls back to `getOption("width")`, so widening inside
`print.compare_ard()` would stretch the section rules to match.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`print.compare_ard()` coerced the comparison tables with `as.data.frame()` and
truncated them with `utils::head(n = 10)`. The tables are already `card`
objects -- `.compare_columns()` builds them with `inner_join()`, which
preserves the class -- so printing them directly gives tibble formatting with
the scalar list-column values shown, and `n = 10` reports the withheld rows in
the footer instead of silently dropping them.
`dplyr::as_tibble()` is not used here: it strips the `card` class, which sends
the `stat.x`/`stat.y`/`difference` list-columns back to printing as
`<dbl [1]>` rather than their values.
Note that pillar formats numbers to `pillar.sigfig` (3) significant digits,
where `as.data.frame()` used `getOption("digits")` (7), so near-identical
values now render alike in the side-by-side columns. The `difference` column
still carries the full `all.equal()` magnitude.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
cli's builtin theme gives `h3` a `margin-top` of 1, so every "Rows in `x`/`y` that do not appear in ..." sub-header was preceded by a blank line -- both under the "Mis-matched Rows" rule and between the two row blocks. A `cli_div()` theme zeroes it; the div closes itself when the method exits. `h1` keeps its `margin-top`, so the "Mis-matched Rows" and "Comparison Results" rules still separate from what precedes them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hard-coded `n = 10` came from the `utils::head(n = 10)` it replaced, but it diverges from the mis-matched row blocks, which use a plain `print()`, and it overrides whatever the user set `tibble.print_min`/`tibble.print_max` to. The default prints every row up to `tibble.print_max` (20) and truncates to `tibble.print_min` (10) beyond that, so long tables are still bounded -- the 45-row block in the truncation test still reports `# i 35 more rows`. The only change is that the 12-row comparison tables now print in full. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ddsjoberg
merged commit Sep 7, 2026
0fc9567
into
malanbos:fix/print-compare-ard-missing-rows
1 check failed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to pharmaverse#607, targeting that PR's branch rather than
main. The one-line fix in pharmaverse#607 is correct — this adds the pieces the review flagged.1. Print the mis-matched rows through the ARD print method
compare_ard()landed 2026-05-04 (ee86fef, pharmaverse#552), back when scalar list-columns printed as<chr [1]>under tibble —as.data.frame()was the workaround for that. The pillar/tibble print rewrite landed 2026-08-06 (69c1c00, pharmaverse#594), andcard_list_shaft()prints scalar list-column elements as their values now. The coercion has been vestigial since.The frames are still
cardobjects:.compare_rows()isselect()+anti_join(), and dplyr's defaultdplyr_reconstructpreserves a custom tibble subclass through both (verified on dplyr 1.2.1 — nodplyr_reconstruct.cardmethod is needed or defined). So dropping the coercion gives us:#607as it stands prints the whole frame, so two large ARDs disagreeing on thousands of keys flood the console. tibble prints 10 rows once there are more than 20, with an honest# i N more rowsfooter. Better than ahead(n = 10)that drops rows silently.group1_level/variable_levelare list-columns; baseprint.data.frame()only reached their values viaformat.default()'spaste(collapse = ", ")fallback.The comparison tables get the same treatment -- they are
cardobjects too (.compare_columns()builds them withinner_join()), andprint(x, n = 10)reports the withheld rows in the footer instead ofutils::head()silently dropping them.dplyr::as_tibble()is deliberately not used: it strips thecardclass, which sendsstat.x/stat.y/differenceback to printing as<dbl [1]>instead of their values.One caveat worth a look: pillar formats to
pillar.sigfig(3) significant digits whereas.data.frame()usedgetOption("digits")(7). Values differing below the 3rd significant digit now render alike side by side --1.5vs1.500001becomes1.5vs1.50. Thedifferencecolumn still carries the fullall.equal()magnitude. Settingpillar.sigfiginside the print method would restore the old precision if you want it.2.
NEWS.mdAdded under
# cards 0.9.0.9000, with the(#605, @malanbos)attribution, so it isn't a manual step at merge time.3. Test coverage
expect_output()assertion outside of any snapshot. The regression here is "output silently missing", which is exactly what a wholesalesnapshot_accept()will swallow. The>80rows exist in only one of the two ARDs, so they are never part of the comparison tables — seeing>80on stdout means the mis-matched row block itself printed.cliwrites to stderr, so it is not captured.4. Widen the console in the snapshot tests
testthat pins
widthto 80, which split the ~120-character comparison tables into two column chunks:withr::local_options(width = 200)at test scope prints each table in one piece.expect_snapshot()does not re-pinwidth, so the option survives it.Test-only on purpose:
withris in Suggests, and in a real sessionclifalls back togetOption("width"), so widening insideprint.compare_ard()would stretch the section rules to match. Under testthatcli.widthis pinned separately, so the rules stay 80 characters and the mis-matched row blocks are byte-identical. Net effect is 34 fewer snapshot lines, since unchunking drops the repeated row-index columns — which also takes some of the sting out of the fixture-size point below.Not done
The review also suggested narrowing the snapshot fixtures (dropping
by = "ARM") to cut churn from the incidentalstatcomparison values. I left them: every other test intest-print.Rusesard_tabulate(ADSL, by = "ARM", variables = "AGEGR1"), and consistency within the file seemed worth more than the ~20 lines. Happy to change it if you disagree.Verification
Full
testthat::test_local()suite passes locally (one pre-existing R-version skip).stylerreports no changes onR/print.Rortests/testthat/test-print.R.🤖 Generated with Claude Code