Changelog
Source:NEWS.md
hvtiPlotR 2.8.0
hvtiPlotR now needs ggplot2 4.0.0 or later. The four themes pass
ink,paper,accentandheader_familytotheme_gray(), and ggplot2 added all four arguments in 4.0.0.DESCRIPTIONpreviously allowed 3.5.0, where every theme failed with an “unused argument” error.Five arguments take a US spelling beside the British one:
color_colinhv_spaghetti(),line_colorinplot.hv_spaghetti(),node_colorsinhv_sankey(),colorsinhv_ppt_series()andcolorinmake_footnote(). The US name is the documented one;colour_col,line_colour,node_colours,coloursandcolourkeep working as aliases, and giving both spellings two different values is an error. Every existing positional call binds as before, since the new names come last (or after...). An abbreviated name that now matches both spellings is an error, somake_footnote(col = ),hv_spaghetti(col = )andhv_sankey(node_col = )must spell the argument out.metacarriescolor_colandnode_colorsbeside the British elements, which keep the same value.scale_color_hv()is now the primary name of the role-color scale added in 2.7.18, andscale_colour_hv()stays as an alias of it, the way ggplot2 pairsscale_color_*()andscale_colour_*(). Existing calls keep working.Documentation, messages and comments now use US spelling (“color”, “gray”, “analyzed”). Exported argument names are unchanged. Output changes where it carried a British word:
print()of anhv_survivalorhv_followupreads “analyzed”, of anhv_spaghetti“Color col”, and the missing-values warning “analyzing”. Gray defaults such as"grey80"are now spelled"gray80", which R draws identically.tools/check-us-spelling.shholds the line in CI.theme_hv_ppt_dark()andtheme_hv_ppt_light()now fall back from Arial to Helvetica when a plot is printed with no graphics device open and the default device ispdf()orpostscript(), as underRscriptandR CMD check. The check trusted Arial whenever no device was open, thenprint()openedpdf(), which stopped with “invalid font type”. This broke thesave_ppt()example on the server.hv_eda_pages()leaves out patient identifiers whenvars = NULL. A column namedccfid,patid,patientid,studyid,subjectid,recordidorcaseid(each also with_,numorno), or any name holdingmrn(mrn_num,pt_mrn), was drawn like a study variable: a numeric one as a scatter of row numbers, a text one as bars with one level per patient.meta$ignoredlists what was left out, and naming a column invarsstill draws it. A bare trailingidis not taken, socarotidandsteroidstay. The stems match the hvtiRtemplates EDA identifier rule.
save_ppt() no longer depends on the study folder
template now defaults to the template shipped in inst/extdata (hv_ppt_template.pptx) instead of the study-relative "../graphs/RD.pptx". That path pointed at each study’s copy of the legacy tp.RD-R-ppt-template.pptx, which went out of date the day it was copied, and a call without template failed wherever the copy was missing. The legacy 4:3 slide (10 in wide) also could not hold the default panel_box, which ends at 11.46 in; the bundled 16:9 template fits it and is updated with the package.
The master template is the blue deck in the Analysis Team SharePoint library (Templates/sl.template_hvti_blue.current.pptx). The bundled hv_ppt_template.pptx is refreshed from it, example slides removed, which adds its Title and Table and Title and Chart layouts. To use the master directly, set options(hvtiPlotR.ppt_template = "<synced path>"), for example in .Rprofile; an explicit template argument still wins, and without the option the bundled copy is used.
A light-room template, hv_ppt_template_light.pptx, is now bundled beside it for use with theme_hv_ppt_light(). Unlike the existing Yahoo slide template LIGHT ROOM.pptx, it carries no example slides, so they do not lead every deck it produces.
Breaking: powerpoint has no default. It was "../graphs/pptExample.pptx", which wrote into whatever graphs/ sat beside the working directory. Name the output file in every call.
hvtiPlotR 2.7.18
New: hv_role_palette(), scale_fill_hv() and scale_colour_hv()
The house color rule for EDA and follow-up figures, as an opt-in scale. An event is vermillion #D55E00, censored is blue #0072B2, missing is light gray #CCCCCC, and every other level takes a colorblind-safe color in order: the light series of hv_ppt_palette() (Okabe-Ito without yellow and sky blue), then Paul Tol’s muted palette without its sand and cyan. Every color but the missing gray clears 2:1 against white. When a panel carries the event or censored level, blue and vermillion leave the rotation so nothing else is mistaken for them.
The scales color each panel from its own levels, so one page & scale_fill_hv(event = "Dead") suits a page where only some panels carry the event. A real NA takes the missing gray (na.value). A panel with more levels than the colorblind-safe palettes hold is drawn in ggplot2’s default hue with a warning; hv_role_palette() called directly stops instead. Design: dev/specs/2026-09-27-hv-palette-design.md.
Vignettes: installed tarball halved, 17.1 MB to 8.9 MB
The installed vignettes embed every figure as a PNG, and plot-functions alone was 15.7 MB of the 17.1 MB tarball. Figures now render at 1x instead of retina 2 when the vignettes are built for the package, which quarters their pixels. The pkgdown site sets IN_PKGDOWN and keeps retina 2. Nothing is removed; figures look slightly softer on a high-density screen when read through vignette().
Vignettes: unused bibliography removed
vignettes/hviPlotR.bib is gone. No vignette declared it as a bibliography or cited any of its seven entries, and seven of its URLs had moved or died, which urlchecker::url_check() reported at every release gate since at least 2.7.15. It now reports none.
hvtiPlotR 2.7.17
New: a set of goodness-of-follow-up panels
hv_followup_panels() prepares one hv_followup() per death indicator and per non-fatal event over one shared study window: the window starts on 1 January of origin_year, where hv_followup() draws its diagonal, and ends at the last operation. The close date is given, or estimated from the data, and meta$close_source says which. Every check runs first: every missing column named in one error, a panel name used twice, indicators that are not 1/0, and an origin that puts operations before it or outside 1900 to next year. plot.hv_followup_panels() returns one bare ggplot per panel, points at alpha 0.5. The checks and the window move here from the dp-gfup template in hvtiRtemplates, whose EDA report draws the same panels through this.
Stacked plot.hv_eda() bars: the legend matches the stack again (#155)
Since 2.7.15 a stacked categorical plot.hv_eda() reversed its fill levels and stacked with reverse = TRUE, to draw missing values on top. The bars landed where they had before, but the legend read upside down against them, and any positional fill scale (scale_fill_brewer(), an unnamed scale_fill_manual(values = )) mapped onto the reversed levels. An ordinal severity palette such as RdYlGn with direction = -1 drew NYHA class IV green and class I red, with no error or warning.
The fill levels now keep their natural order (sorted, with a binary column’s "0" before "1"), and missing values go on top through the group aesthetic instead, so the legend and the stack read top-down in the same order and a positional palette maps level 1 to its first color, as in 2.7.13. The segments themselves draw where they did in 2.7.15 and 2.7.16. The one exception is the NA key: ggplot2 always lists it last in the legend, while the missing segment stacks on top. Callers who reordered a palette to suit 2.7.15’s reversed levels will see those colors swap back.
Documentation
hv_eda_pages() now has a worked example in the “Plot Functions” vignette, “Several variables to a page”, and a row in its constructor table. The SAS migration guide gains an “EDA postage stamps” section and lookup rows mapping tp.dp.EDA_barplots_scatterplots.R and its varnames variant to it, and their single-panel Function_DataPlotting() to hv_eda(), which the guide had never mapped. Both were required by CONTRIBUTING.md when the function was added in 2.7.16.
hvtiPlotR 2.7.16
New: paginated EDA sections
hv_eda_pages() prepares one section of an EDA report: the continuous variables, or the categorical variables as percentages or as counts, one hv_eda() panel each. plot.hv_eda_pages() lays them out as patchwork pages of ncol by nrow panels and returns the list, each page naming its variables in attr(page, "variables") for a caption. The percent and count sections share one binning of the x column, whole years when it is fractional, so their bars line up. Continuous points default to alpha 0.5. vars = NULL takes every column, and a list with missing names reports all of them in one error. Pagination moves here from the dp-postage template in hvtiRtemplates, whose EDA report and postage-stamp jobs both draw through it.
plot.hv_eda() gains an alpha argument for its points. The default stays 0.4, so existing figures do not change.
hv_atrisk_compose() no longer clips multi-strata tables
The composed table kept the curve’s x-range by adding coord_cartesian(expand = FALSE), which also removed the y padding, so with two or more strata the first and last rows sat on the panel edge and printed half cut off. Only the x sides now skip padding. Single-stratum tables were unaffected.
New: RMST contrast and curve plots
hv_rmst_contrast() and plot.hv_rmst_contrast() draw restricted mean survival time differences as a dot-and-whisker plot, one row per estimator in the order supplied (factor levels respected), with a reference line at zero. hv_rmst_curves() and plot.hv_rmst_curves() draw weighted Kaplan-Meier step curves, one facet per estimator and color by arm. With tau the area under each curve up to tau is shaded and a tau line is drawn; with estimates each facet is annotated with its RMST difference and interval.
Both read the tables of hvtiRpropensity::ps_rmst(), as plain data frames or as the ps_rmst object itself, and add no dependency on that package.
hvtiPlotR 2.7.15
Documentation pass across vignettes, reference pages and README
Prose is tightened throughout, and stale references are corrected: plot-decorators no longer documents a style argument no theme has, and names panel_box rather than panel_left/panel_top; the hvtiPlotR vignette’s quick reference points to hv_nonparametric(), its section and figure references resolve, and the template map lists the R templates behind hv_eda() and hv_alluvial(). The contributing guide’s Track A now teaches the hv_<concept>() constructor and plot.hv_<concept>() method pair, with tests that prove the plot carries data. The parametric and nonparametric dataset pages now describe init as the event-free state, not re-operation, and match the columns actually shipped. DESCRIPTION has a title-case Title and a Description that starts with a capital letter.
Updates to eda_classify_var(), hv_eda(), and plot.hv_eda()
eda_classify_var() takes a type_overrides argument to set a variable’s class by hand ("Cont", "Cat_Num" or "Cat_Char"), and a unique_bound argument: a numeric column with any value above it is classified "Cont" whatever its number of distinct values. hv_eda() passes both through and keeps NA values so the plot can show them.
plot.hv_eda() gains loess_cutoff, which skips the smooth when the y-variable has fewer distinct values than the cutoff, and group_bars, which draws grouped bars instead of stacked ones. Missing values in stacked bar plots are handled better.
hvtiPlotR 2.7.14
New hv_correlation_matrix(): the scatter-plot matrix of proc corr plots=matrix
Lower-triangle scatter panels over any set of numeric variables, pairwise deletion per panel, and the coefficient matrix in $tables$coefficients. sample_correlation_data() is its companion. It is the plot half of the dc-tables job in the hvtiR job catalog; the coefficient table with Fisher intervals is hvtiRtables::hv_correlation_table().
ggsankey installs with hvtiPlotR
ggsankey moves from Suggests to Imports. The exported plot.hv_sankey() method requires it at runtime, so installers now resolve davidsjoberg/ggsankey through the existing Remotes entry instead of leaving a newly installed hvtiPlotR unable to draw Sankey plots.
hvtiPlotR 2.7.13
Numbers at risk are counted at the report time, not carried forward to it
hv_survival()$tables$risk overstated n.risk at any report time that fell between two observed event or censoring times. Released in 2.7.12 and earlier; the risk row under a Kaplan-Meier figure was too large whenever nobody happened to be observed at exactly the reported year.
km_risk_table() walked the survfit() summary and, for each report time, took the last row at or before it. That row’s n.risk is the count just before that row’s time, not before the report time, so everyone who left the risk set in the interval between was still being counted. The gap grows with the follow-up gaps: on a cohort where the nearest observation before year 10 is year 8, the year-10 row reported the year-8 number.
Past the end of follow-up it did not degrade, it froze. With no summary row after the last observation, every later report time inherited the final row for ever. On a 300-subject cohort censored administratively at year 20, the default report_times asked for year 25 and the table answered 57 at risk; the correct answer is none. Between-time drift was an off-by-one or two per row, but this was the whole tail of the table.
It now counts the analyzed subjects whose observed follow-up is at least the report time, delegating to the same .atrisk_table() helper that hv_atrisk() draws from. The table and the panel beneath the curve can no longer disagree, and the count is taken over the analyzed cohort – the one meta$n_obs reports, after incomplete rows are excluded – rather than over survfit’s rows.
Numbers change; shape does not. $tables$risk keeps its strata, report_time, n.risk columns, its stratum order and its row order, so nothing downstream needs to move. Figures carrying an at-risk row will show smaller, correct numbers at some report times.
Regression tests cover the between-observation case, the exact-boundary case and stratum ordering, stratified and not.
hvtiPlotR 2.7.12
groups multipliers are validated across the sample generators
Follow-up to 2.7.11, which validated groups in the newly added sample_hazard_cohort() but left its siblings alone. They had the same gap.
A groups multiplier that is negative, zero, NA or Inf did not stop any of these generators. It produced a full-looking data frame carrying NA/NaN estimates, or – for a zero multiplier – a silently meaningless arm in which nobody ever has an event. Measured before the fix:
| generator | c(A = 1, B = -0.5) |
c(A = 1, B = 0) |
c(A = 1, B = NA) |
|---|---|---|---|
sample_hazard_data() |
NaN rows | infinite scale, no events | NA rows |
sample_hazard_empirical() |
NA rows | infinite scale, no events | obscure survfit error |
sample_survival_difference_data() |
NaN rows | infinite scale, no events | NA rows |
sample_nnt_data() |
NaN rows | NA rows | NA rows |
sample_nonparametric_curve_data() |
NaN rows (via log()) |
-Inf (via log()) |
NA rows |
sample_nonparametric_curve_points() |
negative half-life, no NA | NA rows | NA rows |
None of that is obviously wrong on inspection; it renders as a plausible figure. All six now error instead, via the shared validator introduced in 2.7.11. sample_survival_difference_data() and sample_nnt_data() inherit the check by delegating to sample_hazard_data(), and a test pins that delegation.
sample_nonparametric_curve_points() scales its two half-lives by the multiplier, so a negative one yields a negative half-life and a full data frame with no NA in it at all – the quietest case of the set. It documents the groups contract via @inheritParams, so leaving it unvalidated would have had its man page promise a guarantee the function did not keep.
The validator is renamed .check_group_multipliers() (from .check_hazard_groups()) because sample_nonparametric_curve_data() passes its multiplier through log() rather than dividing a Weibull scale – the contract generalises, the name should too. Internal; no exported name changes.
Behavior change. Calls that previously returned NA-laden data now error. No test, example or vignette in the package passed such a value, so nothing here changed, but downstream code relying on the old silent behavior will now see an error – which is the intent.
@param groups on all six now states the contract: names present, non-empty and distinct; multipliers finite and greater than zero.
hvtiPlotR 2.7.11
sample_hazard_cohort(): at-risk tables under hazard figures
New export. hv_atrisk() counts subjects still under observation, so it cannot work from sample_hazard_data() or sample_nnt_data() — both are prediction grids with no subjects in them. That left hv_hazard() and hv_nnt() figures with no honest way to carry a numbers-at-risk table, and the tempting fix — simulating a fresh cohort at figure-assembly time — produces counts that did not come from the same draw as the curve above them. Nothing in the rendered output distinguishes the two.
sample_hazard_empirical() was already drawing exactly the right cohort and discarding it one line after fitting its Kaplan-Meier overlay to it. sample_hazard_cohort() returns that cohort: subject-level time and status (plus group when groups is supplied), from the same seeded draw. Refitting KM to it reproduces sample_hazard_empirical()’s estimates exactly, and a test asserts that invariant so the two cannot drift apart.
arms <- c("No Takedown" = 1.0, "Takedown" = 0.65)
coh <- sample_hazard_cohort(n = 400, time_max = 10, groups = arms)
haz <- hv_hazard(sample_hazard_data(n = 400, time_max = 10, groups = arms),
group_col = "group")
hv_atrisk_compose(
plot(haz),
hv_atrisk(coh, time = "time", status = "status", group = "group",
report_times = c(0, 2, 4, 6, 8, 10))
)Note that sample_hazard_data() is an analytic Weibull curve at the same parameters, not a fit to this cohort. The two share a generative model, not an estimation step, so such a figure should not be captioned as a model fitted to these subjects.
No behavior changes. .hp_km_binned() was refactored onto the shared .hp_draw_cohort() helper with the random-number draw order preserved, so sample_hazard_empirical() output is byte-identical and no snapshots move.
hvtiPlotR 2.7.10
Test helper: reference lines must draw something
Fixes #114. Test infrastructure only; no exported function changes.
expect_plot_has_data() exempted GeomHline, GeomVline, GeomAbline and GeomBlank from its row check by geom class alone. That is right for a literal geom_hline(yintercept = 0), whose single row says nothing about the plotted data, but plot.hv_sankey() maps its dashed vlines to the data with aes(xintercept = ...), so that layer could silently collapse to zero rows and still pass.
- Reference-line geoms are now exempt from
min_rowsbut must carry at least one row. A decoration that drew nothing is a defect whichever way the geom was given its intercept. -
GeomBlankis exempt outright and is now named separately, since drawing nothing is what it is for. - Note that the mapping cannot be used to tell the two forms apart: ggplot2 builds
geom_hline(yintercept = 0)asaes(yintercept = yintercept)over a one-row frame, so both forms carry a mapping. - New
test_plot_data_helper.Rpins all three cases — a mapped reference line that drew nothing, a literal one that drew its single row, and an emptygeom_blank()— andplot.hv_sankey()now has a data-carrying assertion. Suite at 1684 passing tests.
hvtiPlotR 2.7.9
Lint debt
Work on #89. Nothing here changes what any function does; the suite is unchanged at 1629 passing tests.
- Whitespace, commas, semicolons, braces and one over-long
@importFromline brought into line with.lintr. The two multi-line anonymous functions inhv_upset()andhv_venn()are now written as a single-line predicate passed tovapply(), which reads better than the braces lintr wanted. -
plot.hv_eda()no longer assignsy_col_name, which nothing read. The y label comes frommeta$y_label. - Tests that build a plot inside a helper function now use the
.datapronoun inaes(), so the linter can see the column references. - Two false positives are marked with
# nolintand a note saying why:hv_consort_start()takes a bare column name by design, and the survival times in.hp_km_binned()are read inside a formula, whichcodetools::checkUsage()does not walk. - The naming lints are cleared and the lint workflow now gates:
LINTR_ERROR_ON_LINTistrue, solintr::lint_package()must return zero before a push..lintrstates its three deviations from lintr’s defaults and the reason for each: line length 120,object_length35 because six exportedsample_*generators are longer than 30 and renaming an export is a breaking change, andSNAKE_CASEaccepted alongsidesnake_casefor the score-scale constants. - The design-matrix locals in
sample_covariate_balance_data()are nowx_mat/x_mc/x_mt, and one vignette variable isccf_ppt_plot.makeFootnote()and itsfootnoteTextargument keep their camelCase, being the public API of the release before the rename.
hvtiPlotR 2.7.8
Behavior change
theme_hv_poster()now setslegend.position = "none", matchingtheme_hv_manuscript(),theme_hv_ppt_dark()andtheme_hv_ppt_light(). It previously inheritedtheme_grey()’s"right", which made it the one theme in the family that drew a key. House style names a series by annotation on every output target, so the poster theme was the outlier, not the rule, and the docs had been describing four themes as though they all behaved this way. A poster figure that relied on the automatic legend will now render without one; passlegend.position = "right"through..., or chain+ theme(legend.position = "right"), to restore it.A test now asserts the property across the theme family as a set rather than one theme at a time, discovering the exported
theme_hv_*()functions rather than listing them. A hard-coded list reads as a set-wide contract while only checking the themes that existed when it was written, which is the gap that let the poster theme drift; a fifth theme is now covered without anyone remembering to add it.
Documentation
Corrected two overstatements introduced in 2.7.7, both of the same shape: a claim that held for most cases stated as though it held for all.
hv_ppt_series()andvignettes/plot-decorators.qmdsaid everytheme_hv_*()setslegend.position = "none". Three do:theme_hv_manuscript(),theme_hv_ppt_dark()andtheme_hv_ppt_light().theme_hv_poster()never set it and inherited ggplot2’s"right". The docs were corrected first to describe that, and the theme has since been changed to match the other three (see Behavior change above), which makes the original “everytheme_hv_*()” wording true rather than merely accurate about an inconsistency.hv_ppt_palette()said the reordering puts the strongest contrast first. That is true of the first entry in the dark ordering only. Measured against each ordering’s own background, neither ordering is monotonic in contrast ratio, and black is deliberately last in the light ordering while carrying the highest ratio of any color here (21:1 on white). The@detailsnow say what the ordering actually guarantees, which is that each ordering leads with its highest-contrast hue, and give the real reason black is last: house style draws annotation in the theme’s ink, so a black series would be confusable with the label naming it.
hvtiPlotR 2.7.7
New features
hv_ppt_series()bundles a PowerPoint theme with matching color and shape scales into one object you add to every plot in a deck, so the per-slide finishing step is defined once instead of copied per figure. Atheme()cannot do this on its own: it governs the non-data ink and nothing that draws from the data, so no theme element sets a series color, and passing a layer to atheme_hv_*()call errors rather than styling anything, since that...is forwarded straight totheme(). Colors and shapes belong to the scales. The return value is a plain list, and ggplot2’s+unrolls it, so it composes exactly like a theme. Color and shape both map the grouping column, since a projector flattens color differences that read cleanly on a monitor. The house-stylelegend.position = "none"is left alone; passlegend.positionthrough...when a draft wants a key.hv_ppt_palette()returns those colors as a character vector, so a figure that needs them outside the decorator reaches for the one definition instead of pasted hex codes. Six Okabe-Ito colorblind-safe hues, reordered per background: high-luminance first on a dark slide, darker first on a light one. Annotation is not one of these uses; a label takes the theme’s ink to match the axis, white on a slide and black in a manuscript.
Bug fixes
- The Arial fallback in
theme_hv_ppt_dark()/theme_hv_ppt_light()now covers text geoms as well as theme elements. ggplot2 4.0 resolves a text geom’sfamilythrough the theme’sgeomelement, whichtheme_grey()seeds frombase_familyalongsidetext, so patchingtextalone leftgeom_text()andannotate("text", ...)still asking the device for Arial. Onpostscript()/pdf()that is fatal (“invalid font type”), not merely a substituted glyph, so a PPT-themed plot carrying an annotation could not be drawn on those devices at all. House style names series by annotation rather than by a legend, which makes this the common case; no existing example or test drew a text geom on a PPT theme, which is why it went unseen.
hvtiPlotR 2.7.6
Bug fixes
hv_survival()andhv_followup()no longer report a cohort size that differs from the one actually analyzed. Both silently dropped incomplete rows –survfit()omits them, andhv_followup()filtered withcomplete.cases()– while$metaandprint()kept reportingnrow(data). A 20-row input with two missing follow-up times fitted 18 patients but reported 20. Both now exclude incomplete rows explicitly, warn once naming the columns responsible, and reportn_obs/n_patientsas the analyzed cohort alongsiden_inputandn_excluded.print()shows the split whenever anything was excluded. The event panel requires more columns than the death panel, so the two can hold different cohorts; each is filtered and warned about separately, and the event panel’s counts are reported asn_event_patients/n_event_excluded.The
hv_followup()event panel no longer misclassifies death-before-event as a non-fatal event. The state was taken from the event flag alone, andgf_build_event_frame()never received the death time, so it structurally could not order the two: a patient dying at year 1 with an event recorded at year 2 was labeled “Non-fatal event”, contradicting the documented “death before non-fatal event” state. The event time is now compared againstdeath_time_col, and a flagged event only counts as non-fatal when it strictly precedes death. Ties go to death.The missing-data contract is now package-wide, in two deliberately different shapes. Analysis constructors (
hv_survival(),hv_followup()) exclude incomplete rows, because the fit they wrap already has — reporting a cohort the fit never saw is simply wrong. Plot constructors (hv_trends(),hv_spaghetti(),hv_stacked(),hv_balance(),hv_longitudinal()) account for them instead: they warn once, recordn_missingin$meta, andprint()shows the count — but they do not filter. Pre-filtering there would also swallow ggplot2’s own “Removed n rows” warning, which legitimately fires for values outside a zoomed scale range and not only for missing ones.hv_mirror_hist()already reportedn_droppedand keeps its existing diagnostics table.Grouping and label columns are treated as a third case: they must be complete, and a missing value there is now an error. Such a value does not drop the row — it silently merges it into an
"NA"group that reads as a real series in the legend, or, forhv_spaghetti()’sid_col, fuses unrelated subjects into a single trajectory. Affectsgroup_colinhv_trends()/hv_stacked(),id_colandcolour_colinhv_spaghetti(), andvariable_col/group_colinhv_balance();hv_longitudinal()already behaved this way.hv_longitudinal()now rejects negative and non-finite counts, which previously rendered as downward bars without warning, and rejects missing time or series labels, which collapsed into anNAcategory that read as a real group on the axis and in the legend.hv_trends()no longer loses non-numeric time points. Summary positions came fromas.numeric(names(tapply(...))), so factor labels and dates becameNAand their summary points vanished from the plot. The original x type is now preserved through aggregation.hv_survival()andhv_atrisk()now rejectreport_timescontainingNA,NaN,Inf, or negative values. Previously these were accepted and survived into the risk and report tables as plausible-looking rows rather than failing:NAand a negative time each reported 100% survival, andInfreported the final survival estimate at timeInf. The failure was silent, so the table was misleading rather than obviously wrong. Both functions now share a.check_report_times()validator. The check applies on every entry point, includinghv_atrisk()called with anhv_dataobject or a precomputed risk table – those route through.select_report_times(), where bad values were previously dropped with an “ignored” warning instead of erroring.report_times = NULLstill means “every time already in the table”.hv_upset()andhv_venn()now reject duplicated entries inintersectandsets. A repeated name mangled the column toA.1, producing two contradictory “A only” regions and a nonsense “A & A” self-intersection.
Documentation
- The SAS migration guide had four factual errors beyond the follow-up section, all now corrected against the functions they describe: the UpSet section still named
ComplexUpsetas the backend (it has beenggupsetsince 2.2.0); the alluvial section credited an internalto_lodes_form()reshape thathv_alluvial()does not perform; the covariate-balance section described the wide SAS export as “one column per time-point” when the columns are comparisons (Before match/After match); and the longitudinal section claimedhv_longitudinal()accepts a patient-level frame when it takes pre-aggregated counts. The survival section also conflated five plot types with four SAS output flags, and now names which is which.
Packaging
.Rbuildignorenow excludesvignettes/.quarto/, rendered vignette.html, and the.superpowers/tool-state directory. These added 230 paths to the source tarball and triggered anR CMD checkwarning about rendered artifacts invignettes/..Rbuildignorealso excludes.lintr. It is a development-time lint configuration, not part of the installed package, and shipping it drew anR CMD checkNOTE about hidden files. It stays tracked in git.The
Authors@Remail now matches theMaintainerfield (john.ehrlinger@gmail.com). The two had disagreed, whichR CMD checkreports as a DESCRIPTION meta-information NOTE.Three
\donttestexamples wrote PDFs into the working directory (survival.pdf,trends.pdf,fig.pdf).--as-cranexecutes those blocks, so the files landed in the check directory and were reported as non-standard. They now write totempdir(), which is what CRAN policy requires of examples regardless of the NOTE.
hvtiPlotR 2.7.5
Bug fixes
-
plot.hv_followup()no longer draws a vertical stem below each patient’s point. The stem was a carryover from the legacytp.dp.gfup.RSAS template; at realistic cohort sizes it smears the point cloud and, because it shares thecolouraesthetic with the points, it also struck a line through every glyph in the legend key.hv_followup()now defaultssegment_drop = 0and the segment layer is omitted entirely when the drop is zero, so both the panel and the legend show bare shapes. Passsegment_drop = 0.2to restore the previous appearance.
hvtiPlotR 2.7.4
Bug fixes
-
theme_hv_ppt_dark()/theme_hv_ppt_light()no longer error on a graphics device that can’t resolve the defaultbase_family = "Arial"(e.g.postscript()/pdf()on Linux, which is whatR CMD check --run-donttestrenders examples with). The active device is checked at draw time – immediately beforeprint()/grid.draw()hand off to ggplot2 – and falls back to"Helvetica"(metrically compatible) only when Arial genuinely can’t be resolved there, with a one-time session message explaining why. Devices that resolve system fonts directly (quartz, cairo, RStudio’s graphics device) are unaffected and continue to render real Arial. No global font registry is modified.
hvtiPlotR 2.7.3
New features
-
save_manuscript()gainsdraft_file/draft_dpiarguments: write an additional raster (typically PNG) copy alongside the primary publisher file in one call. Use this to keep a small, portable draft figure for dragging into a Word manuscript whilefilestays the publisher deliverable (vector PDF/EPS, or raster TIFF) actually submitted to the journal.
hvtiPlotR 2.7.2
New features
-
hv_legend_inside()gains apreferargument: name a corner ("topright"/"topleft"/"bottomright"/"bottomleft") and the legend goes there when that corner is clear, even if another corner is emptier. When the preferred corner is occupied it falls back to the emptiest-corner logic, so the occlusion guard is never given up.
hvtiPlotR 2.7.1
New features
-
hv_legend_inside(): place a plot’s legend in the emptiest panel corner automatically, falling back to an outside position when no corner is clear (e.g. dense multi-curve panels). Coordinates come from the coord’s own transform, socoord_flip()is handled correctly. Apply it after the house theme. See the recipes-book legends chapter.
Dependencies
- Minimum
ggplot2raised to>= 3.5.0(the version that introduced the inside-legend APIhv_legend_inside()uses:legend.position = "inside"withlegend.position.inside/legend.justification.inside).
hvtiPlotR 2.7.0
New features
-
save_manuscript(): save a ggplot at the house manuscript figure size (6 x 4 inches) in one call — the manuscript counterpart ofsave_ppt(). Pair it withtheme_hv_manuscript()for the 12 pt typography; likesave_ppt(), it fixes the output geometry, not the theme. Passdevice = grDevices::cairo_pdfto embed fonts in a PDF where cairo is available.
Documentation
- Cross-reference the companion HVTI ggplot graphics recipes book (https://ehrlinger.github.io/hvtiGraphics/) from the package help page, the
DESCRIPTIONURLfield, the README, and the pkgdown navbar.
hvtiPlotR 2.6.1
Bug fixes
-
theme_hv_poster(),theme_hv_ppt_dark(),theme_hv_ppt_light(): thepaperargument now controls the figure background (plot.backgroundfill). It was hard-coded to"transparent", so passingpaperhad no effect. The PPT themes still default to transparent (the slide background shows through);theme_hv_poster()now honors its"white"default.
Documentation
-
plot.hv_alluvial(): clarify thatshow_yaxis = FALSEis atheme()layer, so a complete theme added afterward (e.g.theme_hv_manuscript()) re-asserts the axis it styles. -
plot.hv_venn(): note the diagram is coordinate-free — do not add an axis-bearing house theme, which pastes spurious axes onto it. Example updated.
hvtiPlotR 2.6.0
New features
-
hv_venn()draws a 2-3 set Venn diagram of overlapping set memberships — the small-set-count companion tohv_upset(), reading the same logical / 0-1 set-membership columns. It returns an object carrying a$tables$regionscount table (one row per Venn region), andplot()renders a bare ggplot via ggvenn that you finish with+ theme_hv_*. For more than three sets, usehv_upset().
Dependency: ggvenn added to Imports.
hvtiPlotR 2.5.0
New features
-
hv_atrisk()renders a numbers-at-risk table as a bare ggplot panel. It takes a survival-family object that carries$tables$risk(e.g.hv_survival()), a precomputedstrata/time/ntable, or a subject-level data frame plustime/status/groupcolumn names (the path for the curve-data constructorshv_nonparametric,hv_ordinal,hv_hazard, which carry no risk table). Missingreport_timesare derived from the observed time range. -
hv_atrisk_compose()stacks a survival curve over anhv_atrisk()panel, aligning the table’s x-range to the curve’s and composing them with patchwork. Decorate both panels with patchwork’s&.
hvtiPlotR 2.4.0
New features
-
hv_sankey(): withnode_levels = NULL(default), the node order is now derived from the data instead of the first column’s factor levels. It spans every label in anycluster_colscolumn and seats each child next to its parent (the coarser-K cluster holding most of its members), so flows stay uncrossed and the spurious grayNAboxes are gone. An explicitnode_levelsis still used as given but must cover every observed label. -
plot.hv_sankey():alphais split intoflow_alpha(default0.5) for the flows and column guides andlabel_alpha(default0.3) for the label fill, matching the publication look.alphais deprecated; if given, it sets both and emits a message. -
plot.hv_sankey(): newgroup_labels, a named vector mapping acluster_colsvalue to a milestone label (e.g.c(C7 = "5 groups")). That column’s x-axis tick reads"<col>\n<label>"; unlisted columns stay bare. -
plot.hv_sankey(): defaultnode_coloursmap labels to Set1 in node order, recycling with a warning when labels outnumber colors. -
plot.hv_alluvial(): newshow_yaxis(defaultTRUE). SetFALSEto blank the y-axis title, text, ticks, and line for a clean patient-flow look; the geometry is untouched and you can still add your owntheme()after.
hvtiPlotR 2.3.4
Bug fixes
-
save_ppt(): correctedpanel_boxdefaults tolist(width = 8.79, height = 4.422, left = 2.67, top = 1.29).
hvtiPlotR 2.3.3
Bug fixes
-
save_ppt(): the white box behind plots is gone again. The earlier fix only cleared the officer placeholder’s fill;rvg::dml()still defaults tobg = "white", so the DrawingML graphic painted its own opaque white canvas rectangle behind the (transparent) plot. Bothdml()calls (plots and consort diagrams) now passbg = "transparent", so the slide-template background shows through cleanly on dark/blue decks. -
theme_hv_ppt_dark()andtheme_hv_ppt_light()now default tobase_family = "Arial",base_size = 32, with Arial 32 Bold axis tick labels and Arial 40 Bold axis titles — matching the canonical CORR deck (driveline-infections, slide 6). Axis titles scale atbase_size * 1.25. (Slide-title fonts are controlled by the PowerPoint template, not the theme.) Override at the call site as usual, e.g.theme_hv_ppt_dark(base_size = 28).
Changes
-
save_ppt()now defaultspanel_boxto the standard CORR fixed-panel rectanglelist(width = 8.88, height = 4.51, left = 2.58, top = 1.63), so every deck anchors the plot panel at the same slide coordinates by default (AATS-style placement). Passpanel_box = NULLto restore the legacy fixed-width/height/left/topplacement. Because the default now routes plots throughhv_ph_location(), save_ppt suppresses the benign “font family ‘Arial’ not found in PostScript font database” warning from the grob-measurement device (the slide graphic still embeds Arial via systemfonts).
Documentation
-
save_ppt()examples now show the recommended preview-light / save-dark workflow (build withtheme_hv_ppt_light()for the IDE viewer, swap totheme_hv_ppt_dark()before saving) and add a no-y-axis-label slide (labs(y = NULL)). - Dropped the
family = "mono"snippet from the theme docs and switched the executed Kaplan–Meier examples off the Arial PPT themes (totheme_hv_poster()), soR CMD checkexamples stay warning-free on hosts without Arial installed (a cause of the failing CI run). - Declared
xml2inSuggests(used by the save_ppt white-box test), fixing the “unstated dependencies in ‘tests’” check WARNING. - Added a small (~16 KB) dark-background test template at
inst/extdata/hv_ppt_template.pptx, derived from the canonical CORR deck (master, layouts, and theme only — content slides, notes, comments, media, and document metadata stripped). Reach it withsystem.file("extdata", "hv_ppt_template.pptx", package = "hvtiPlotR")to trysave_ppt()against an authentic dark slide master.
hvtiPlotR 2.3.2
Documentation (#70)
- Vignette clarity pass: added structural grounding to all five vignettes — two to four sentences before every code chunk on what the recipe does, when to reach for it, and (for bare/raw plots) what to look for. Applied the
memory/vignette-clarity-pass.mdworkflow developed for TemporalHazard 1.0.3. - Corrected four factual claims surfaced by review: the dashed-threshold recommendation in the SAS migration guide (
geom_hline(), notannotate("hline")); thetheme_hv_poster()font claim (the theme does not enforce a sans-serif face); thetheme(legend.position = "none")layout-space claim (no space is reserved); and thetheme_hv_manuscript()“no title” claim (the theme does not blankplot.title).
hvtiPlotR 2.3.1
Documentation (#69)
- Package-wide voice rewrite of every prose surface — README, vignettes, NEWS, slide deck, roxygen, DESCRIPTION
Title:— against thewriting-voice.mdspec. Prose only; no API or behavioral change. - Fixed stale
hv_theme()string-key references ("dark_ppt","ppt","light_ppt","manuscript","poster") in the main tutorial and SAS-migration vignettes. That dispatcher was removed in 2.1.0; the vignettes now name the actualtheme_hv_*()functions. - Fixed missing “to” in the main tutorial’s introduction (“is simplify” → “is to simplify”).
- README now lists all five vignettes (
sas-migration-guidewas missing) and the “Migrating from plot.sas” section points at the dedicated migration vignette. - Synced a copy of
writing-voice.mdinto the repo root for future documentation work.
hvtiPlotR 2.3.0
CONSORT patient flow tracking and diagram (#67)
Two-class API for CONSORT flow diagrams built from patient-level data.
Tracker lifecycle:
-
hv_consort_start(data, patient_id, label, pass_col)— initializes a tracker with one row per patient; all patients begin as screened. -
hv_consort_exclude(tracker, label, col, ..., excl_label, pass_col)— adds an exclusion stage via formula rules (condition ~ "Reason string"). First-matching formula wins; gating on the prior stage is automatic. -
hv_consort_summary(tracker)— returns a data frame with N included and N excluded per stage; suitable for methods-section tables. -
hv_consort_patients(tracker, stage, reason)— returns patient IDs at any stage, or the subset excluded for a specific reason.
Diagram:
-
hv_consort(tracker, side_box, cex, width, height)— auto-derivesordersandside_boxfrom tracker metadata and callsconsort::consort_plot().side_box = "all"(default) includes every exclusion column; pass a character vector to select specific columns. -
plot.hv_consort(x)— renders the diagram via theconsortplot method. -
save_ppt()now acceptshv_consortobjects, producing an editable DrawingML vector object in the output.pptx.
Sample data:
-
sample_consort_data(n, seed)— reproducible three-stage cardiac surgery tracker for demos and testing.
Dependency: consort (>= 0.2.0) added to Imports.
Documentation
- New worked CONSORT Patient Flow Diagram section in the Plot Functions vignette, covering the tracker workflow, the rendered diagram, the audit helpers, and
save_ppt()export (#68). - New “What’s New in v2.x” slide deck shipped as Quarto source at
inst/slides/hvtiPlotR-whats-new.qmd— an onboarding overview of the v2.x API redesign, theme rename, and PowerPoint export, with a README “Slides” section pointing to it (#67).
hvtiPlotR 2.2.0
New S3 methods for hv_data objects (#64)
Three standard-R S3 verbs are now implemented for every hv_data subclass:
-
summary()— prints the standard one-screen header, then walks the object’s$tablesslot and prints each named auxiliary table with a header. Callers get risk tables, report tables, and diagnostics without reaching for$tablesdirectly. Subclasses can override with a curated layout. -
autoplot()— re-exports ggplot2’sautoplot()generic and dispatches to the registeredplot.<subclass>()method. Callers who prefer the ggplot2-ecosystem verb (broom,ggfortify, andggsurvfitall use it) can writeautoplot(km)in place ofplot(km). Extra args forward to the subclassplot(). -
as.data.frame()— returns the$dataslot via standardas.data.frame()coercion instead of the$dataaccessor.
No breaking changes.
UpSet plot backend swap (#62)
plot.hv_upset() is now backed by ggupset rather than ComplexUpset. The intersection-size bar chart is a standard ggplot; themes apply via +, and ggplot2::ggplot_build() works on the output. When set_size = TRUE (the default) a manual set-size sidebar is composed via patchwork.
Breaking changes:
- The
base_annotationsparameter has been removed. To recolor the intersection bars by an external grouping variable, passfill_col = "<column>"toplot(); for a single fixed color passbar_fill = "<colour>". - The
min_sizeparameter has been replaced byn_intersections(top-N by frequency or degree — seesort_by). ComplexUpset’s size-threshold semantics are not preserved. -
encode_setsandsort_intersectionsparameters have been removed (the new equivalents aresort_byandset_size_sort). - Themes now apply via
+, not&.&still works on the patchwork composite whenset_size = TRUE.
Other changes:
- The
ComplexUpsetImport has been dropped;ggupset (>= 0.4.0)is added in its place.patchworkhas been promoted from Suggests to Imports. -
hv_upset()adds a.Procedureslist-column to its stored$dataforscale_x_upset()to consume. - The four
upset_*vignette chunks no longer carry a Windows skip gate; ggupset renders cleanly on every CI runner. -
tests/testthat/test_example_plot_data.Randtests/testthat/test_plot_integration.Rdrop theirtryCatch / skip_if_theme_incompatibilitywrappers — the upset output now passesexpect_plot_has_data()like every other plot.
hvtiPlotR 2.1.0
Theme API redesign
The four theme functions are now named
theme_hv_manuscript(),theme_hv_poster(),theme_hv_ppt_dark(), andtheme_hv_ppt_light(), matching ggplot2’stheme_bw()/theme_grey()naming.-
Each theme follows the
theme_bw()contract: passbase_size/base_familyto control typography, then forward any extra named theme element through...to override. Examples:theme_hv_manuscript(legend.position = "right") theme_hv_ppt_dark(axis.text.y = element_text(family = "mono")) The
hv_theme()dispatcher has been removed. Call the named theme function directly.The
bold = TRUE,mono_y = TRUE, andtitle_sizekwargs on the PPT themes have been removed. Express the equivalent overrides through...(e.g.axis.text = element_text(face = "bold")oraxis.text.y = element_text(family = "mono")).The previous names (
hv_theme_manuscript(),hv_theme_poster(),hv_theme_dark_ppt(),hv_theme_light_ppt(),hv_theme_ppt(),theme_man(),theme_manuscript(),theme_poster(),theme_ppt(),theme_dark_ppt(),theme_light_ppt()) remain as deprecated aliases that emit a one-shot deprecation warning and forward to the new theme function. Plan to remove in v3.0.0.
Tests
- Added
tests/testthat/helper-plot-data.Randtests/testthat/test_example_plot_data.R. Every@examplesand vignette plot path is exercised throughggplot2::ggplot_build(), asserting that non-decorator layers carry observation rows, expected geoms appear, and grouped/stratified plots preserve their groups. Catches the “plot rendered but contains no data” defect without a graphics device.
hvtiPlotR 2.0.1
Bug fixes
-
save_ppt(): theofficer::ph_location()call that places each plot on its slide now usesbg = "transparent"so the placeholder rectangle’s fill no longer shows up as an opaque white box behind the plot on dark PowerPoint templates. Previously, officer’s default ph_location shape had a white fill; against a blue-gradient slide template that appeared as a visible white rectangle larger than the ggplot panel. The ggplot itself is unchanged; only the containing shape’s fill is now transparent so the slide template background shows through any area outside the panel. -
hv_theme_light_ppt(): panel background is nowfill = "transparent"(was"white"). Saving alight_ppt-themed plot into a dark PowerPoint template previously showed as an opaque white rectangle inside the panel; with a transparent fill the slide template shows through. Add+ theme(panel.background = element_rect(fill = "white"))if you specifically need an opaque white panel.
Documentation
- Package-level help topic (
?hvtiPlotR) rewritten to cover the v2.0.0 feature set: two-step workflow with runnable example, fixed-panel geometry subsection (hv_ggsave_dims()/hv_ph_location()/save_ppt(panel_box=)), legacy single-call API,hv_dataclass introspection, scope and versioning, vignette index. - README “Utilities” table now lists
hv_ggsave_dims()andhv_ph_location(), and documentssave_ppt(panel_box=). -
save_ppt()example rewritten to show the recommendedhv_theme("light_ppt")preview workflow saving into a dark PPT template withpanel_box = list(width = 8.88, height = 4.51, left = 2.58, top = 1.29)and ascale_x_continuous(breaks = seq(0, 400, 100), expand = c(0, 0))decorator. -
hvtiPlotR.qmdcolor-guidance section updated to reflect that ColorBrewer palettes are accessed via ggplot2’s built-inscale_colour_brewer()rather than a directRColorBrewerdependency.
hvtiPlotR 2.0.0
First stable release of the hv_* API. This consolidates the 2.0.0.9001–2.0.0.9013 dev cycle into a tagged release that internal users can anchor against for bug reports and reproducibility. Subsequent releases will advance the semantic version (2.0.1, 2.1.0, 3.0.0) directly; no .9xxx pre-release suffixes.
New features — fixed-panel geometry
The dominant theme of this release is making the panel content area (the rectangular data region, excluding axes/titles/legend/margins) directly addressable, so figures stay visually aligned across output devices and across slides in a deck even when axis-label widths differ.
-
hv_ggsave_dims(plot, width, height, units = "in"): computeggsave()width/heightthat preserve a fixed panel content area regardless of surrounding chrome. Returns a named list shaped to splat intoggsave()viado.call(ggsave, c(list(filename = ..., plot = p), dims)). Units are length-only ("in","cm","mm") since the sizing device is PDF. -
hv_ph_location(plot, panel_width, panel_height, panel_left, panel_top, units = "in"): computeofficer::ph_location()width/height/left/topvalues that anchor a ggplot’s panel to a fixed rectangle on a slide, regardless of axis-label width. Measures asymmetric chrome (left/right/top/bottom of the panel) viaggplotGrob()and returns per-plot placement so the panel lands at the same slide coordinates on every slide. Warns if plot chrome extends past the left or top slide edge. -
save_ppt(..., panel_box = list(width, height, left, top)): new optional argument. When supplied, per-slide placement is computed viahv_ph_location()so every slide anchors the panel at the given rectangle; on dark PPT themes where the panel fill is visible, the panel no longer shifts between slides. Whenpanel_box = NULL(default), the fixedwidth/height/left/toparguments are used (legacy behavior).
New features — PPT theme polish
-
hv_theme_dark_ppt(bold = TRUE)andhv_theme_light_ppt(bold = TRUE): applyface = "bold"to axis text and axis titles.
Behavior changes — PPT themes
-
hv_theme_dark_ppt()andhv_theme_light_ppt():- Legend is now hidden by default (
legend.position = "none"). PowerPoint figures are typically annotated directly on the panel; override with+ theme(legend.position = "right")when needed. - Axis ticks now face inside the panel (
axis.ticks.length = -half_line/2 pt) for the AATS-style inset look. - Axis-text and axis-title margins are now scaled from
base_sizevia ggplot2’shalf_line = base_size / 2convention, so spacing stays proportional whenbase_sizechanges. Previous unscaled defaults produced cramped labels atbase_size = 32. -
hv_theme_light_ppt()gains explicitaxis.text,axis.line,panel.background(fill"white", color"black", linewidth 1), andaxis.tickselements so the light theme structurally mirrors the dark theme’s explicit-chrome approach (just with inverted colors).
- Legend is now hidden by default (
Build / infrastructure
-
.Rbuildignoreis now tracked in git (previously.gitignored). Three latent regex bugs fixed: anchored.gitignorepattern, stripped inline# ...comments from five patterns (which had silently never matched), and fixed^vignettes/*_files$→^vignettes/.*_files$so_files/output dirs actually get excluded from the build. -
vignettes/_quarto.ymlnow tracked withembed-resources: falseat the project level, making the small-HTML / separate_files/rendering behavior explicit and preventing accidental repo bloat.
Dependency trim (from the merged trends_plots branch)
- Dropped
RColorBrewer(inline Set1 hex incluster_sankey_plot()and vignettes) andgridExtra(marrangeGrob()→patchwork::wrap_plots()in the EDA multi-panel PDF pattern) from Suggests. - Dropped
assertthatfrom Imports (save_ppt()now uses basestop()withcall. = FALSE). -
vignettes/hvtiPlotR.qmdnow documentshaven::read_xpt()(andhaven::read_sas()) for importing SAS data, with a CSV-fallback path for users without SAS access.
hvtiPlotR 2.0.0.9010
Bug fixes
-
plot-functions.qmd: UpSet plot chunks (upset_data,upset_basic,upset_fill,upset_era) now skip on Windows (eval: !expr .Platform$OS.type != "windows"). ComplexUpset’s patchwork rendering crashes the Rscript subprocess on the Windows CI runner (os error 232 / “pipe being closed”), so the examples are shown only on macOS and Linux where they render reliably.
hvtiPlotR 2.0.0.9009
Documentation
-
plot-functions.qmd: updated both mirror-histogram decorated examples to follow standard ggplot2 mirror-plot conventions:- Added
scale_y_continuous(labels = abs)so the y-axis displays absolute counts on both halves of the panel. - Replaced hard-coded y-coordinates in
annotate()calls withy = Inf/y = -Infplusvjust, anchoring each group label near the top/bottom panel edge regardless of dataset size. - Replaced hard-coded label strings (
"SAVR","TF-TAVR", etc.) withmh$meta$group_labels[1]/[2], so the annotations track the labels supplied to the constructor.
- Added
hvtiPlotR 2.0.0.9008
Documentation
- Vignettes: all
hv_theme("manuscript")calls inside R code blocks replaced withhv_theme("poster")to demonstrate non-default theme options. Prose references (e.g. migration guide comparison table) are preserved unchanged. Theme-specific sections inplot-decorators.qmd(## Manuscript,## Manuscript PDF) continue to demonstratehv_theme("manuscript"). -
plot-functions.qmd: added explicit Bare plot subsections to the six sections that previously chained directly from build to decoration — mirror-histogram (binary-match and IPTW), trends (cases/year), spaghetti, nonparametric temporal curve, nonparametric ordinal curve, and longitudinal participation counts. Each section now follows the three-step pattern:- build with
hv_*(), (2) render bare ggplot withp <- plot(obj), - decorate with
scale_*()+hv_theme("poster").
- build with
-
plot-functions.qmd: UpSet section split into## Bare plot(showingplot(hu)) and## Applying a theme(showingplot(hu) & hv_theme("poster")), with an explanatory note that the patchwork&operator is required.
hvtiPlotR 2.0.0.9007
Breaking changes
- All exported functions, S3 methods, and class names have been renamed from the
hvti_prefix to the shorterhv_prefix. No backward-compatible aliases are provided. Update all call sites:-
hvti_survival()→hv_survival() -
hvti_trends()→hv_trends() -
hvti_mirror_hist()→hv_mirror_hist() -
hvti_spaghetti()→hv_spaghetti() -
hvti_balance()→hv_balance() -
hvti_alluvial()→hv_alluvial() -
hvti_sankey()→hv_sankey() -
hvti_nonparametric()→hv_nonparametric() -
hvti_ordinal()→hv_ordinal() -
hvti_followup()→hv_followup() -
hvti_longitudinal()→hv_longitudinal() -
hvti_stacked()→hv_stacked() -
hvti_eda()→hv_eda() -
hvti_hazard()→hv_hazard() -
hvti_nnt()→hv_nnt() -
hvti_upset()→hv_upset() -
hvti_theme()→hv_theme() -
hvti_survival_difference()→hv_survival_difference() -
is_hvti_data()→is_hv_data() - Class strings
"hvti_*"→"hv_*"(affectsinherits()checks) - The package name (
hvtiPlotR) is unchanged.
-
hvtiPlotR 2.0.0.9006
Documentation
-
hv_mirror_hist()$tables$diagnostics: corrected return documentation from “a data frame of matched/unmatched counts per group” to accurately describe the actual type — a named list of diagnostic summaries whose contents vary by mode (binary-match vs weighted IPTW). All keys are now enumerated in the@returnblock. -
$metakeys inhv_mirror_hist()return docs updated to include all keys actually stored (score_col,group_col,match_colwere missing). - Added
@family Propensity Score & Matchingtohv_mirror_hist()andplot.hv_mirror_hist(), creating automatic bi-directional “See also” cross-links consistent with all otherhv_*constructor/plot pairs. -
plot.hv_mirror_hist()@returnnow describes composability with+(scales, limits, labels,hv_theme), matching the pattern used in all other updated plot methods. -
plot.hv_mirror_hist()@seealsoexpanded with descriptive text for each linked function, matching the richer pattern used elsewhere.
hvtiPlotR 2.0.0.9005
Tests
- Added
test_trends_plot.R(37 tests):$metaslot keys and values,$tables$summarystructure and row counts, factor level order preservation,print.hv_trendsoutput and invisible return, and full parameter coverage forplot.hv_trends(se,span,point_size,point_shape,alpha,smoother, grouped vs ungrouped mapping, composability withhv_theme). - Added
test_spaghetti_plot.R(25 tests):$metaslot keys and values,id_col/y_colabsent error cases,print.hv_spaghettioutput with and withoutcolour_colbranch and invisible return, and full parameter coverage forplot.hv_spaghetti(add_smooth,smooth_se,line_colour,line_width,alphaboundaries,y_labelserror cases,smooth_method, grouped vs ungrouped mapping, composability withhv_theme). - Added
test_hv_data.R(27 tests):new_hv_data()structure contract, input validation errors,is_hv_data()TRUE/FALSE for all relevant types,print.hv_database-class output and invisible return, subclass dispatch (verifyingprint.hv_spaghettioverridesprint.hv_data), andplot.hv_datafallback error with subclass name in message.
hvtiPlotR 2.0.0.9004
Breaking changes
-
hv_mirror()renamed tohv_mirror_hist()for naming consistency with the underlying plot type. The old name is registered as an@aliasesentry so?hv_mirrorstill resolves to the correct help page.
New features
-
hv_mirror_hist()is now searchable via?mirror_histogram,?hv_mirror,??propensity,??IPTW, and??matchingthrough@aliasesand@concepttags in its documentation.
Documentation
- All
hv_*constructors andplot.hv_*methods now carry@familytags; the help system and pkgdown reference both show bi-directional “See also” links between each constructor and its plot method. -
@returnon every constructor now explicitly says “callplot()to render” and links to the correspondingplot.hv_*method. -
@seealsoentries across all constructors and plot methods now include descriptive text explaining the role of each linked function. -
@examplesin all main plot methods include a\dontrun{}block demonstratingggplot2::theme_set(hv_theme_manuscript())for applying the publication theme globally,scale_colour_brewer()/scale_fill_brewer()for multi-group color palettes, and a pointer tovignette("plot-decorators", package = "hvtiPlotR").
hvtiPlotR 2.0.0
Breaking changes — new S3 constructor API
All plot functions have been replaced by a two-step S3 workflow:
# Step 1: construct & validate
obj <- hv_*(data, ...) # returns c("hv_<concept>", "hv_data")
# Step 2: render
plot(obj, ...) + # bare ggplot — no scales, labels, or theme
scale_color_manual(...) +
labs(...) +
hv_theme("manuscript")The old single-call functions (mirror_histogram(), survival_curve(), etc.) are removed. This is a clean break; no deprecated wrappers.
Constructor → old function mapping
| New constructor | Removed function(s) |
|---|---|
hv_mirror_hist() |
mirror_histogram() |
hv_balance() |
covariate_balance() |
hv_stacked() |
stacked_histogram() |
hv_survival() |
survival_curve() |
hv_nonparametric() |
nonparametric_curve_plot() |
hv_ordinal() |
nonparametric_ordinal_plot() |
hv_followup() |
goodness_followup() + goodness_event_plot()
|
hv_trends() |
trends_plot() |
hv_spaghetti() |
spaghetti_plot() |
hv_longitudinal() |
longitudinal_counts_plot() + longitudinal_counts_table()
|
hv_alluvial() |
alluvial_plot() |
hv_sankey() |
cluster_sankey_plot() |
hv_eda() |
eda_plot() |
hv_upset() |
upset_plot() |
hv_hazard() | hazard_plot() |hv_survival_difference() | survival_difference_plot() |hv_nnt() | nnt_plot() |The legacy hazard helpers (hazard_plot(), survival_difference_plot(), nnt_plot()) remain exported but are marked Superseded — use the S3 constructors above instead.
Multi-type constructors
Two constructors replace pairs of old functions via a type = argument on plot():
-
hv_longitudinal—plot(x, type = "plot")(bar chart, waslongitudinal_counts_plot()) orplot(x, type = "table")(text panel, waslongitudinal_counts_table()). -
hv_followup—plot(x, type = "followup")(death panel, wasgoodness_followup()) orplot(x, type = "event")(non-fatal event panel, wasgoodness_event_plot()).
New base class
- Added
hv_dataS3 base class (R/hvti-data.R). Everyhv_*constructor returnslist(data=, meta=, tables=)with classc("hv_<concept>", "hv_data"). -
new_hv_data()— internal constructor; validatesdata(data.frame),meta(named list),tables(list),subclass(character). -
print.hv_data()— fallback print method; shows class, dimensions, and slot names. -
plot.hv_data()— fallback plot method; stops with a helpful message if no concreteplot.hv_*()is registered. -
is_hv_data()— exported predicate.
Documentation
- Rewrote
help.Rpackage-level documentation to describe the new two-step constructor +plot()workflow and list allhv_*()constructors. - Updated
_pkgdown.ymlreference index: grouped by constructor family, withplot.*andprint.*S3 methods explicitly listed. - Updated all vignettes (
plot-functions.qmd,sas-migration-guide.qmd,plot-decorators.qmd) to use the new API throughout. - Updated
sas-migration-guide.qmdkey-concepts section and template reference table. - Fixed all stale
@seealsocross-references and orphaned old-API docblocks in every migrated R source file.
Tests
- Added
tests/testthat/test_hazard_plot.R— full validation suite forsample_hazard_data,sample_hazard_empirical,sample_life_table,hv_hazard,hv_survival_difference, andhv_nnt(column checks, CI bounds, layer structure, multi-group, non-default column names, input validation, print output, empirical/reference validation). - Added
tests/testthat/test_nonparametric_plots.R— full suite forsample_nonparametric_curve_data,sample_nonparametric_curve_points,nonparametric_curve_plot,sample_nonparametric_ordinal_data,sample_nonparametric_ordinal_points, andnonparametric_ordinal_plot. Includes probability-sum-to-1 invariant test for ordinal grades. - Added
tests/testthat/test_survival_derived.R— full suite forsample_survival_difference_data,sample_nnt_data, and legacysurvival_difference_plot/nnt_plot. Covers NA-NNT at t≈0 edge case and cross-function time-grid consistency. - Added
tests/testthat/test_cluster_sankey.R— full suite forsample_cluster_sankey_dataandcluster_sankey_plot. Validates the hierarchical merge tree (C9=A → C2=A) and that each Ck has exactly k levels. - Added
tests/testthat/test_pipeline.R— end-to-end pipeline tests coveringsurvival_curve → hv_theme → save_ppt, multi-slide list pipelines, built-in dataset usability,eda_classify_varedge cases (logical vector, all-NA, length-1), and composed multi-layer plots. - Added snapshot test to
test_kaplan_meier.Rforsurvival_curvereport_tableat fixed seed; added all-censored and single-observation edge-case tests. - Added snapshot test to
test_mirror_histogram.Rfor diagnostics at fixed seed. - Added
slide_titleslength-mismatch test totest_save_ppt.R. - Added
make_footnoteprefix-parameter tests totest_footnote.R.
Documentation
- Fixed
save_ppt()argument names throughout all vignettes:plot =→object =,filename =→powerpoint =. Also added correcttemplate =andslide_titles =arguments where missing. - Fixed critical roxygen bug in
sample_mirror_histogram_data(): doc block used##'(silently ignored by roxygen2) instead of#', so the function had no generated.Rdfile. Converted all##'→#', modernised\code{}→ backtick syntax, and added@examples. - Added
@examplesto all five theme functions:hv_theme(),hv_theme_manuscript(),hv_theme_dark_ppt(),hv_theme_light_ppt(), andhv_theme_poster(). - Expanded thin (2-line)
@examplesblocks for four sample-data helpers:sample_life_table(),sample_nonparametric_curve_points(),sample_nonparametric_ordinal_points(), andsample_longitudinal_counts_data(). - Fixed
km$survival_plotandkm$risk_tableaccessor patterns invignettes/plot-decorators.qmd:survival_curve()returns a ggplot with attributes, not a named list. Replaced withkm(the returned object IS the survival plot) andattr(km, "risk_table"). - Fixed patchwork operator-precedence bug in
vignettes/plot-decorators.qmd:p_ms | p_km_ms + plot_layout(...)→(p_ms | p_km_ms) + plot_layout(...). - Added
patchworktoSuggestsinDESCRIPTION(required byvignettes/plot-decorators.qmd). - Rewrote package-level help page (
help.R/?hvtiPlotR) to document all 57 exported functions, organized by category. - Expanded “Saving figures” section in
vignettes/sas-migration-guide.qmdwith correctsave_ppt()single- and multi-slide examples. - Added
ggplot2::geom_line(..., linewidth = 1.5)(replacing deprecatedsize =) and updatedremotes::install_github()(replacingdevtools::install_github()) invignettes/hvtiPlotR.qmd.
Input validation improvements
-
upset_plot()— added binary-column type check. ComplexUpset silently produces broken plots whenintersectcolumns contain non-binary values; the function now errors with a clear message listing the offending columns before handing off to ComplexUpset. -
sample_stacked_histogram_data()— addedstart_yearvalidation (previouslyn_yearsandn_categorieswere checked butstart_yearwas not; a non-integer or non-finite value produced silently nonsensical output). -
trends_plot()— movedmatch.arg(summary_fn)to after the data-frame and column checks, so users see a cleardata/ column error rather than an opaque'arg' should be one of...message when bothdataandsummary_fnare wrong. -
validators.R— added two scalar-parameter helpers:.check_scalar_positive()(finite, positive) and.check_scalar_nonneg()(finite, non-negative).cb_validate_params()incovariate-balance.Rnow delegates all four parameter checks to these helpers, eliminating 28 lines of bespoke validation code.
Architecture
-
.NP_SIMconstant list (nonparametric-curve-plot.R) — lifted the seven simulation tuning constants (eta_intercept,logit_shift,cont_baseline,cont_scale,cont_sigma,eff_frac_prob,eff_frac_cont) from local variables insample_nonparametric_curve_data()and from hard-coded defaults in.np_sample_bins()into a single file-level private list. Both functions now reference.NP_SIM$*— change once, updates all simulation paths.
Bug fixes / API consistency
-
Standardized
alpharange to[0, 1]across all plot functions. Previouslysurvival_curve(),covariate_balance(),mirror_histogram(),spaghetti_plot(), andgoodness_followup_death_plot()/goodness_followup_event_plot()used(0, 1](rejectingalpha = 0), whilealluvial_plot()used[0, 1]. All functions now accept[0, 1]—alpha = 0(fully transparent) is a valid ggplot2 value and should not be an error. -
Added
.check_alpha()shared validator inR/validators.R. Enforcesalpha ∈ [0, 1]withcall. = FALSEand is called from every plot function that accepts analphaargument. -
call. = FALSEsweep — everystop()call in the package now includescall. = FALSEso error messages never expose internal function names to callers. -
Expanded shared validators (
R/validators.R) to 11 files (up from 3). All ofalluvial-plot.R,covariate-balance.R,eda-plots.R,goodness-followup.R,hazard-plot.R,kaplan-meier.R,longitudinal-counts-plot.R,mirror-histogram.R,nonparametric-curve-plot.R,nonparametric-ordinal-plot.R,spaghetti-plot.R,stacked-histogram.R,trends-plot.R, andupset-plot.Rnow delegatedata.frame, column-presence, numeric-column, and alpha checks to.check_df(),.check_cols(),.check_col(),.check_numeric_col(), and.check_alpha(). Error messages use consistent wording across all entry points.
Code quality
- Named all simulation tuning constants in
sample_nonparametric_curve_data()and the internal helper.np_sample_bins():eta_intercept,logit_shift,cont_baseline,cont_scale,cont_sigma,eff_frac_prob,eff_frac_cont. Magic numbers replaced throughout the single-curve, multi-group, and binned-data-summary code paths. - Named all simulation tuning constants in
sample_nonparametric_ordinal_data()andsample_nonparametric_ordinal_points():a_first,a_step,eta_intercept. Every occurrence of-0.2,0.5, and1.2replaced by the named constant. - Extended edge-case test coverage:
-
test_kaplan_meier.R: added fivesurvival_curveerror tests for non-numerictime_col, invalidevent_colvalues (character instead of 0/1/logical), andalphaat 0, > 1, and < 0. -
test_hazard_plot.R: added graceful-handling test for an empty data frame (correct columns, zero rows) — confirms ggplot renders without error. -
test_mirror_histogram.R: added error test for non-numericscore_col.
-
hvtiPlotR 2.0.0.9000
- Added
eda_plot()— exploratory barplot/scatterplot for a single variable. Auto-detects variable type ("Cont","Cat_Num","Cat_Char") and dispatches to scatter + LOESS + rug (continuous) or stacked/filled bar (categorical).NAvalues are shown as an explicit"(Missing)"fill level. Returns a bare ggplot object for composition withscale_fill_*,scale_colour_*,labs(),annotate(), and [hv_theme()]. PortsFunction_DataPlotting()fromtp.dp.EDA_barplots_scatterplots.R. - Added
eda_classify_var()— replicates theUniqueLimittype-detection logic fromBarplot_Scatterplot_Function.R: classifies a vector as"Cont","Cat_Num", or"Cat_Char". - Added
eda_select_vars()— subsets and reorders a data frame by a character vector or space-separated string of column names. ReplacesOrder_Variables()and theMod_Data <- dta[, Order_Var]pattern fromtp.dp.EDA_barplots_scatterplots_varnames.R. - Added
sample_eda_data()— mixed-type cardiac-surgery registry simulation (binary, ordinal, character-categorical, and continuous variables) for demonstratingeda_plot()andeda_select_vars(). - Reorganized
inst/: movedpar_cst.xptandnpar_cst.xpttoinst/extdata/(standard R package location for bundled data files); removed unreferenced presentation and test artifacts (*.pptx,*.pdf,*.sasscratch files). - Extended
nonparametric_curve_plot()examples: added dual-Y-axis example (Example 10,\dontrun) usingscale_y_continuous(sec.axis = ...); notedcll_p95/clu_p95column availability for 95 % CI (Example 2) and per-group shape mapping viascale_shape_manual()(Example 4). - Extended
nonparametric_ordinal_plot()examples: added pre-operative severity comparison example grouping combined Mild/Moderate/Severe cohorts throughnonparametric_curve_plot(). - Split vignette into three:
hvtiPlotR.qmd(SAS migration guide),plot-functions.qmd(per-function reference with worked examples),plot-decorators.qmd(composition grammar:scale_*,labs(), themes, and saving to manuscript PDF, poster PDF, and editable PowerPoint viasave_ppt()).
hvtiPlotR 1.1.0
- Added
survival_curve()— Kaplan-Meier and Nelson-Aalen survival analysis returning five plot types (survival, cumulative hazard, hazard, log-log, life/RMST) plus risk and report tables. Ports the SAS%kaplanand%nelsontmacros fromtp.ac.dead.sas. - Added
sample_survival_data()— realistic exponential survival simulation with administrative censoring and optional treatment strata. - Added
goodness_followup()— goodness-of-follow-up scatter plot showing actual vs. potential follow-up per operation year, with optional non-fatal event panel. - Added
sample_goodness_followup_data()— simulates an operative cohort with operation dates, follow-up times, competing events, and death. - Added
covariate_balance()— standardized mean difference dot-plot for propensity-score matching or weighting diagnostics. - Added
sample_covariate_balance_data()— patient-level logistic simulation with greedy 1:1 caliper matching; SMDs computed before and after matching. - Added
stacked_histogram()andsample_stacked_histogram_data()— stacked or filled histogram of a numeric variable by group. - Improved
mirror_histogram()sample data (sample_mirror_histogram_data()) to use a realistic logistic propensity-score model with greedy 1:1 caliper matching and optional ATE IPTW weights; extreme-PS patients naturally go unmatched. - Added
hv_plot()dispatcher supporting"mirror_histogram","stacked_histogram", and"covariate_balance"plot types. - Added
hv_theme()dispatcher for"manuscript","ppt","dark_ppt", and"poster"themes. - Enabled roxygen Markdown (
Roxygen: list(markdown = TRUE)) so**bold**, backtick code spans, and[pkg::fn()]cross-references render correctly in Rd help pages. - Added
survivalto packageImports. - Updated package-level documentation (
help.R) to reflect all current exported functions and sample-data generators.
hvtiPlotR 0.2.2
- Fixed deprecated ggplot2 syntax (
size->linewidthinelement_lineandelement_rect). - Removed empty
save.hvtiplotrfunction. - Fixed
theme_dark_pptto pass all parameters totheme_grey. - Updated documentation for data objects.
- Updated README to reference
officerpackage instead of deprecatedReporteRs.