Skip to contents

hvtiPlotR 2.8.0

  • hvtiPlotR now needs ggplot2 4.0.0 or later. The four themes pass ink, paper, accent and header_family to theme_gray(), and ggplot2 added all four arguments in 4.0.0. DESCRIPTION previously allowed 3.5.0, where every theme failed with an “unused argument” error.

  • Five arguments take a US spelling beside the British one: color_col in hv_spaghetti(), line_color in plot.hv_spaghetti(), node_colors in hv_sankey(), colors in hv_ppt_series() and color in make_footnote(). The US name is the documented one; colour_col, line_colour, node_colours, colours and colour keep 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, so make_footnote(col = ), hv_spaghetti(col = ) and hv_sankey(node_col = ) must spell the argument out. meta carries color_col and node_colors beside 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, and scale_colour_hv() stays as an alias of it, the way ggplot2 pairs scale_color_*() and scale_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 an hv_survival or hv_followup reads “analyzed”, of an hv_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.sh holds the line in CI.

  • theme_hv_ppt_dark() and theme_hv_ppt_light() now fall back from Arial to Helvetica when a plot is printed with no graphics device open and the default device is pdf() or postscript(), as under Rscript and R CMD check. The check trusted Arial whenever no device was open, then print() opened pdf(), which stopped with “invalid font type”. This broke the save_ppt() example on the server.

  • hv_eda_pages() leaves out patient identifiers when vars = NULL. A column named ccfid, patid, patientid, studyid, subjectid, recordid or caseid (each also with _, num or no), or any name holding mrn (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$ignored lists what was left out, and naming a column in vars still draws it. A bare trailing id is not taken, so carotid and steroid stay. 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.

Regression test for at-risk counts past the end of follow-up

A test now covers the failure mode behind the at-risk fix in 2.7.13: a report time after the last observation must show nobody at risk, not repeat the final count. It was written with that fix but did not reach main.

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_rows but must carry at least one row. A decoration that drew nothing is a defect whichever way the geom was given its intercept.
  • GeomBlank is 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) as aes(yintercept = yintercept) over a one-row frame, so both forms carry a mapping.
  • New test_plot_data_helper.R pins all three cases — a mapped reference line that drew nothing, a literal one that drew its single row, and an empty geom_blank() — and plot.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 @importFrom line brought into line with .lintr. The two multi-line anonymous functions in hv_upset() and hv_venn() are now written as a single-line predicate passed to vapply(), which reads better than the braces lintr wanted.
  • plot.hv_eda() no longer assigns y_col_name, which nothing read. The y label comes from meta$y_label.
  • Tests that build a plot inside a helper function now use the .data pronoun in aes(), so the linter can see the column references.
  • Two false positives are marked with # nolint and 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, which codetools::checkUsage() does not walk.
  • The naming lints are cleared and the lint workflow now gates: LINTR_ERROR_ON_LINT is true, so lintr::lint_package() must return zero before a push. .lintr states its three deviations from lintr’s defaults and the reason for each: line length 120, object_length 35 because six exported sample_* generators are longer than 30 and renaming an export is a breaking change, and SNAKE_CASE accepted alongside snake_case for the score-scale constants.
  • The design-matrix locals in sample_covariate_balance_data() are now x_mat / x_mc / x_mt, and one vignette variable is ccf_ppt_plot. makeFootnote() and its footnoteText argument keep their camelCase, being the public API of the release before the rename.

hvtiPlotR 2.7.8

Behavior change

  • theme_hv_poster() now sets legend.position = "none", matching theme_hv_manuscript(), theme_hv_ppt_dark() and theme_hv_ppt_light(). It previously inherited theme_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; pass legend.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() and vignettes/plot-decorators.qmd said every theme_hv_*() sets legend.position = "none". Three do: theme_hv_manuscript(), theme_hv_ppt_dark() and theme_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 “every theme_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 @details now 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. A theme() 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 a theme_hv_*() call errors rather than styling anything, since that ... is forwarded straight to theme(). 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-style legend.position = "none" is left alone; pass legend.position through ... 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’s family through the theme’s geom element, which theme_grey() seeds from base_family alongside text, so patching text alone left geom_text() and annotate("text", ...) still asking the device for Arial. On postscript() / 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() and hv_followup() no longer report a cohort size that differs from the one actually analyzed. Both silently dropped incomplete rows – survfit() omits them, and hv_followup() filtered with complete.cases() – while $meta and print() kept reporting nrow(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 report n_obs / n_patients as the analyzed cohort alongside n_input and n_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 as n_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, and gf_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 against death_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, record n_missing in $meta, and print() 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 reported n_dropped and 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, for hv_spaghetti()’s id_col, fuses unrelated subjects into a single trajectory. Affects group_col in hv_trends()/hv_stacked(), id_col and colour_col in hv_spaghetti(), and variable_col/group_col in hv_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 an NA category 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 from as.numeric(names(tapply(...))), so factor labels and dates became NA and their summary points vanished from the plot. The original x type is now preserved through aggregation.

  • hv_survival() and hv_atrisk() now reject report_times containing NA, NaN, Inf, or negative values. Previously these were accepted and survived into the risk and report tables as plausible-looking rows rather than failing: NA and a negative time each reported 100% survival, and Inf reported the final survival estimate at time Inf. 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, including hv_atrisk() called with an hv_data object 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 = NULL still means “every time already in the table”.

  • hv_upset() and hv_venn() now reject duplicated entries in intersect and sets. A repeated name mangled the column to A.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 ComplexUpset as the backend (it has been ggupset since 2.2.0); the alluvial section credited an internal to_lodes_form() reshape that hv_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 claimed hv_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

  • .Rbuildignore now excludes vignettes/.quarto/, rendered vignette .html, and the .superpowers/ tool-state directory. These added 230 paths to the source tarball and triggered an R CMD check warning about rendered artifacts in vignettes/.

  • .Rbuildignore also excludes .lintr. It is a development-time lint configuration, not part of the installed package, and shipping it drew an R CMD check NOTE about hidden files. It stays tracked in git.

  • The Authors@R email now matches the Maintainer field (john.ehrlinger@gmail.com). The two had disagreed, which R CMD check reports as a DESCRIPTION meta-information NOTE.

  • Three \donttest examples wrote PDFs into the working directory (survival.pdf, trends.pdf, fig.pdf). --as-cran executes those blocks, so the files landed in the check directory and were reported as non-standard. They now write to tempdir(), 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 legacy tp.dp.gfup.R SAS template; at realistic cohort sizes it smears the point cloud and, because it shares the colour aesthetic with the points, it also struck a line through every glyph in the legend key. hv_followup() now defaults segment_drop = 0 and the segment layer is omitted entirely when the drop is zero, so both the panel and the legend show bare shapes. Pass segment_drop = 0.2 to 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 default base_family = "Arial" (e.g. postscript()/pdf() on Linux, which is what R CMD check --run-donttest renders examples with). The active device is checked at draw time – immediately before print()/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.

Documentation

  • Documentation now follows the composed house style. The package-level documentation moved from R/help.R to R/hvtiPlotR-package.R, and the stale root writing-voice.md was replaced by the generated .claude/house-style.md. No user-facing behavior changed.

hvtiPlotR 2.7.3

New features

  • save_manuscript() gains draft_file/draft_dpi arguments: 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 while file stays the publisher deliverable (vector PDF/EPS, or raster TIFF) actually submitted to the journal.

hvtiPlotR 2.7.2

New features

  • hv_legend_inside() gains a prefer argument: 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, so coord_flip() is handled correctly. Apply it after the house theme. See the recipes-book legends chapter.

Dependencies

  • Minimum ggplot2 raised to >= 3.5.0 (the version that introduced the inside-legend API hv_legend_inside() uses: legend.position = "inside" with legend.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 of save_ppt(). Pair it with theme_hv_manuscript() for the 12 pt typography; like save_ppt(), it fixes the output geometry, not the theme. Pass device = grDevices::cairo_pdf to embed fonts in a PDF where cairo is available.

Documentation

hvtiPlotR 2.6.1

Bug fixes

Documentation

  • plot.hv_alluvial(): clarify that show_yaxis = FALSE is a theme() 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 to hv_upset(), reading the same logical / 0-1 set-membership columns. It returns an object carrying a $tables$regions count table (one row per Venn region), and plot() renders a bare ggplot via ggvenn that you finish with + theme_hv_*. For more than three sets, use hv_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 precomputed strata/time/n table, or a subject-level data frame plus time/status/group column names (the path for the curve-data constructors hv_nonparametric, hv_ordinal, hv_hazard, which carry no risk table). Missing report_times are derived from the observed time range.
  • hv_atrisk_compose() stacks a survival curve over an hv_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(): with node_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 any cluster_cols column and seats each child next to its parent (the coarser-K cluster holding most of its members), so flows stay uncrossed and the spurious gray NA boxes are gone. An explicit node_levels is still used as given but must cover every observed label.
  • plot.hv_sankey(): alpha is split into flow_alpha (default 0.5) for the flows and column guides and label_alpha (default 0.3) for the label fill, matching the publication look. alpha is deprecated; if given, it sets both and emits a message.
  • plot.hv_sankey(): new group_labels, a named vector mapping a cluster_cols value 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(): default node_colours map labels to Set1 in node order, recycling with a warning when labels outnumber colors.
  • plot.hv_alluvial(): new show_yaxis (default TRUE). Set FALSE to 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 own theme() after.

hvtiPlotR 2.3.4

Bug fixes

  • save_ppt(): corrected panel_box defaults to list(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 to bg = "white", so the DrawingML graphic painted its own opaque white canvas rectangle behind the (transparent) plot. Both dml() calls (plots and consort diagrams) now pass bg = "transparent", so the slide-template background shows through cleanly on dark/blue decks.
  • theme_hv_ppt_dark() and theme_hv_ppt_light() now default to base_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 at base_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 defaults panel_box to the standard CORR fixed-panel rectangle list(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). Pass panel_box = NULL to restore the legacy fixed-width/height/left/top placement. Because the default now routes plots through hv_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 with theme_hv_ppt_light() for the IDE viewer, swap to theme_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 (to theme_hv_poster()), so R CMD check examples stay warning-free on hosts without Arial installed (a cause of the failing CI run).
  • Declared xml2 in Suggests (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 with system.file("extdata", "hv_ppt_template.pptx", package = "hvtiPlotR") to try save_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.md workflow developed for TemporalHazard 1.0.3.
  • Corrected four factual claims surfaced by review: the dashed-threshold recommendation in the SAS migration guide (geom_hline(), not annotate("hline")); the theme_hv_poster() font claim (the theme does not enforce a sans-serif face); the theme(legend.position = "none") layout-space claim (no space is reserved); and the theme_hv_manuscript() “no title” claim (the theme does not blank plot.title).

hvtiPlotR 2.3.1

Documentation (#69)

  • Package-wide voice rewrite of every prose surface — README, vignettes, NEWS, slide deck, roxygen, DESCRIPTION Title: — against the writing-voice.md spec. 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 actual theme_hv_*() functions.
  • Fixed missing “to” in the main tutorial’s introduction (“is simplify” → “is to simplify”).
  • README now lists all five vignettes (sas-migration-guide was missing) and the “Migrating from plot.sas” section points at the dedicated migration vignette.
  • Synced a copy of writing-voice.md into 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-derives orders and side_box from tracker metadata and calls consort::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 the consort plot method.
  • save_ppt() now accepts hv_consort objects, 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 $tables slot and prints each named auxiliary table with a header. Callers get risk tables, report tables, and diagnostics without reaching for $tables directly. Subclasses can override with a curated layout.
  • autoplot() — re-exports ggplot2’s autoplot() generic and dispatches to the registered plot.<subclass>() method. Callers who prefer the ggplot2-ecosystem verb (broom, ggfortify, and ggsurvfit all use it) can write autoplot(km) in place of plot(km). Extra args forward to the subclass plot().
  • as.data.frame() — returns the $data slot via standard as.data.frame() coercion instead of the $data accessor.

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_annotations parameter has been removed. To recolor the intersection bars by an external grouping variable, pass fill_col = "<column>" to plot(); for a single fixed color pass bar_fill = "<colour>".
  • The min_size parameter has been replaced by n_intersections (top-N by frequency or degree — see sort_by). ComplexUpset’s size-threshold semantics are not preserved.
  • encode_sets and sort_intersections parameters have been removed (the new equivalents are sort_by and set_size_sort).
  • Themes now apply via +, not &. & still works on the patchwork composite when set_size = TRUE.

Other changes:

  • The ComplexUpset Import has been dropped; ggupset (>= 0.4.0) is added in its place. patchwork has been promoted from Suggests to Imports.
  • hv_upset() adds a .Procedures list-column to its stored $data for scale_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.R and tests/testthat/test_plot_integration.R drop their tryCatch / skip_if_theme_incompatibility wrappers — the upset output now passes expect_plot_has_data() like every other plot.

hvtiPlotR 2.1.0

Theme API redesign

Tests

  • Added tests/testthat/helper-plot-data.R and tests/testthat/test_example_plot_data.R. Every @examples and vignette plot path is exercised through ggplot2::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(): the officer::ph_location() call that places each plot on its slide now uses bg = "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 now fill = "transparent" (was "white"). Saving a light_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_data class introspection, scope and versioning, vignette index.
  • README “Utilities” table now lists hv_ggsave_dims() and hv_ph_location(), and documents save_ppt(panel_box=).
  • save_ppt() example rewritten to show the recommended hv_theme("light_ppt") preview workflow saving into a dark PPT template with panel_box = list(width = 8.88, height = 4.51, left = 2.58, top = 1.29) and a scale_x_continuous(breaks = seq(0, 400, 100), expand = c(0, 0)) decorator.
  • hvtiPlotR.qmd color-guidance section updated to reflect that ColorBrewer palettes are accessed via ggplot2’s built-in scale_colour_brewer() rather than a direct RColorBrewer dependency.

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"): compute ggsave() width/height that preserve a fixed panel content area regardless of surrounding chrome. Returns a named list shaped to splat into ggsave() via do.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"): compute officer::ph_location() width/height/left/top values 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) via ggplotGrob() 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 via hv_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. When panel_box = NULL (default), the fixed width/height/left/top arguments are used (legacy behavior).

New features — PPT theme polish

  • hv_theme_dark_ppt(bold = TRUE) and hv_theme_light_ppt(bold = TRUE): apply face = "bold" to axis text and axis titles.

Behavior changes — PPT themes

  • hv_theme_dark_ppt() and hv_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_size via ggplot2’s half_line = base_size / 2 convention, so spacing stays proportional when base_size changes. Previous unscaled defaults produced cramped labels at base_size = 32.
    • hv_theme_light_ppt() gains explicit axis.text, axis.line, panel.background (fill "white", color "black", linewidth 1), and axis.ticks elements so the light theme structurally mirrors the dark theme’s explicit-chrome approach (just with inverted colors).

Build / infrastructure

  • .Rbuildignore is now tracked in git (previously .gitignored). Three latent regex bugs fixed: anchored .gitignore pattern, 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.yml now tracked with embed-resources: false at the project level, making the small-HTML / separate _files/ rendering behavior explicit and preventing accidental repo bloat.
  • Dropped RColorBrewer (inline Set1 hex in cluster_sankey_plot() and vignettes) and gridExtra (marrangeGrob() → patchwork::wrap_plots() in the EDA multi-panel PDF pattern) from Suggests.
  • Dropped assertthat from Imports (save_ppt() now uses base stop() with call. = FALSE).
  • vignettes/hvtiPlotR.qmd now documents haven::read_xpt() (and haven::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 with y = Inf/y = -Inf plus vjust, anchoring each group label near the top/bottom panel edge regardless of dataset size.
    • Replaced hard-coded label strings ("SAVR", "TF-TAVR", etc.) with mh$meta$group_labels[1] / [2], so the annotations track the labels supplied to the constructor.

hvtiPlotR 2.0.0.9008

Documentation

  • Vignettes: all hv_theme("manuscript") calls inside R code blocks replaced with hv_theme("poster") to demonstrate non-default theme options. Prose references (e.g. migration guide comparison table) are preserved unchanged. Theme-specific sections in plot-decorators.qmd (## Manuscript, ## Manuscript PDF) continue to demonstrate hv_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:
    1. build with hv_*(), (2) render bare ggplot with p <- plot(obj),
    2. decorate with scale_*() + hv_theme("poster").
  • plot-functions.qmd: UpSet section split into ## Bare plot (showing plot(hu)) and ## Applying a theme (showing plot(hu) & hv_theme("poster")), with an explanatory note that the patchwork & operator is required.

hvtiPlotR 2.0.0.9007

Breaking changes

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 @return block.
  • $meta keys in hv_mirror_hist() return docs updated to include all keys actually stored (score_col, group_col, match_col were missing).
  • Added @family Propensity Score & Matching to hv_mirror_hist() and plot.hv_mirror_hist(), creating automatic bi-directional “See also” cross-links consistent with all other hv_* constructor/plot pairs.
  • plot.hv_mirror_hist() @return now describes composability with + (scales, limits, labels, hv_theme), matching the pattern used in all other updated plot methods.
  • plot.hv_mirror_hist() @seealso expanded 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): $meta slot keys and values, $tables$summary structure and row counts, factor level order preservation, print.hv_trends output and invisible return, and full parameter coverage for plot.hv_trends (se, span, point_size, point_shape, alpha, smoother, grouped vs ungrouped mapping, composability with hv_theme).
  • Added test_spaghetti_plot.R (25 tests): $meta slot keys and values, id_col/y_col absent error cases, print.hv_spaghetti output with and without colour_col branch and invisible return, and full parameter coverage for plot.hv_spaghetti (add_smooth, smooth_se, line_colour, line_width, alpha boundaries, y_labels error cases, smooth_method, grouped vs ungrouped mapping, composability with hv_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_data base-class output and invisible return, subclass dispatch (verifying print.hv_spaghetti overrides print.hv_data), and plot.hv_data fallback error with subclass name in message.

hvtiPlotR 2.0.0.9004

Breaking changes

  • hv_mirror() renamed to hv_mirror_hist() for naming consistency with the underlying plot type. The old name is registered as an @aliases entry so ?hv_mirror still resolves to the correct help page.

New features

  • hv_mirror_hist() is now searchable via ?mirror_histogram, ?hv_mirror, ??propensity, ??IPTW, and ??matching through @aliases and @concept tags in its documentation.

Documentation

  • All hv_* constructors and plot.hv_* methods now carry @family tags; the help system and pkgdown reference both show bi-directional “See also” links between each constructor and its plot method.
  • @return on every constructor now explicitly says “call plot() to render” and links to the corresponding plot.hv_* method.
  • @seealso entries across all constructors and plot methods now include descriptive text explaining the role of each linked function.
  • @examples in all main plot methods include a \dontrun{} block demonstrating ggplot2::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 to vignette("plot-decorators", package = "hvtiPlotR").

hvtiPlotR 2.0.0.9001

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()

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, was longitudinal_counts_plot()) or plot(x, type = "table") (text panel, was longitudinal_counts_table()).
  • hv_followup — plot(x, type = "followup") (death panel, was goodness_followup()) or plot(x, type = "event") (non-fatal event panel, was goodness_event_plot()).

New base class

  • Added hv_data S3 base class (R/hvti-data.R). Every hv_* constructor returns list(data=, meta=, tables=) with class c("hv_<concept>", "hv_data").
  • new_hv_data() — internal constructor; validates data (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 concrete plot.hv_*() is registered.
  • is_hv_data() — exported predicate.

Documentation

  • Rewrote help.R package-level documentation to describe the new two-step constructor + plot() workflow and list all hv_*() constructors.
  • Updated _pkgdown.yml reference index: grouped by constructor family, with plot.* and print.* 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.qmd key-concepts section and template reference table.
  • Fixed all stale @seealso cross-references and orphaned old-API docblocks in every migrated R source file.

Tests

  • Added tests/testthat/test_hazard_plot.R — full validation suite for sample_hazard_data, sample_hazard_empirical, sample_life_table, hv_hazard, hv_survival_difference, and hv_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 for sample_nonparametric_curve_data, sample_nonparametric_curve_points, nonparametric_curve_plot, sample_nonparametric_ordinal_data, sample_nonparametric_ordinal_points, and nonparametric_ordinal_plot. Includes probability-sum-to-1 invariant test for ordinal grades.
  • Added tests/testthat/test_survival_derived.R — full suite for sample_survival_difference_data, sample_nnt_data, and legacy survival_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 for sample_cluster_sankey_data and cluster_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 covering survival_curve → hv_theme → save_ppt, multi-slide list pipelines, built-in dataset usability, eda_classify_var edge cases (logical vector, all-NA, length-1), and composed multi-layer plots.
  • Added snapshot test to test_kaplan_meier.R for survival_curve report_table at fixed seed; added all-censored and single-observation edge-case tests.
  • Added snapshot test to test_mirror_histogram.R for diagnostics at fixed seed.
  • Added slide_titles length-mismatch test to test_save_ppt.R.
  • Added make_footnote prefix-parameter tests to test_footnote.R.

Documentation

  • Fixed save_ppt() argument names throughout all vignettes: plot = → object =, filename = → powerpoint =. Also added correct template = and slide_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 .Rd file. Converted all ##' → #', modernised \code{} → backtick syntax, and added @examples.
  • Added @examples to all five theme functions: hv_theme(), hv_theme_manuscript(), hv_theme_dark_ppt(), hv_theme_light_ppt(), and hv_theme_poster().
  • Expanded thin (2-line) @examples blocks for four sample-data helpers: sample_life_table(), sample_nonparametric_curve_points(), sample_nonparametric_ordinal_points(), and sample_longitudinal_counts_data().
  • Fixed km$survival_plot and km$risk_table accessor patterns in vignettes/plot-decorators.qmd: survival_curve() returns a ggplot with attributes, not a named list. Replaced with km (the returned object IS the survival plot) and attr(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 patchwork to Suggests in DESCRIPTION (required by vignettes/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.qmd with correct save_ppt() single- and multi-slide examples.
  • Added ggplot2::geom_line(..., linewidth = 1.5) (replacing deprecated size =) and updated remotes::install_github() (replacing devtools::install_github()) in vignettes/hvtiPlotR.qmd.

Input validation improvements

  • upset_plot() — added binary-column type check. ComplexUpset silently produces broken plots when intersect columns 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() — added start_year validation (previously n_years and n_categories were checked but start_year was not; a non-integer or non-finite value produced silently nonsensical output).
  • trends_plot() — moved match.arg(summary_fn) to after the data-frame and column checks, so users see a clear data / column error rather than an opaque 'arg' should be one of... message when both data and summary_fn are wrong.
  • validators.R — added two scalar-parameter helpers: .check_scalar_positive() (finite, positive) and .check_scalar_nonneg() (finite, non-negative). cb_validate_params() in covariate-balance.R now delegates all four parameter checks to these helpers, eliminating 28 lines of bespoke validation code.

Architecture

  • .NP_SIM constant 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 in sample_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 alpha range to [0, 1] across all plot functions. Previously survival_curve(), covariate_balance(), mirror_histogram(), spaghetti_plot(), and goodness_followup_death_plot() / goodness_followup_event_plot() used (0, 1] (rejecting alpha = 0), while alluvial_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 in R/validators.R. Enforces alpha ∈ [0, 1] with call. = FALSE and is called from every plot function that accepts an alpha argument.
  • call. = FALSE sweep — every stop() call in the package now includes call. = FALSE so error messages never expose internal function names to callers.
  • Expanded shared validators (R/validators.R) to 11 files (up from 3). All of alluvial-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, and upset-plot.R now delegate data.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() and sample_nonparametric_ordinal_points(): a_first, a_step, eta_intercept. Every occurrence of -0.2, 0.5, and 1.2 replaced by the named constant.
  • Extended edge-case test coverage:
    • test_kaplan_meier.R: added five survival_curve error tests for non-numeric time_col, invalid event_col values (character instead of 0/1/logical), and alpha at 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-numeric score_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). NA values are shown as an explicit "(Missing)" fill level. Returns a bare ggplot object for composition with scale_fill_*, scale_colour_*, labs(), annotate(), and [hv_theme()]. Ports Function_DataPlotting() from tp.dp.EDA_barplots_scatterplots.R.
  • Added eda_classify_var() — replicates the UniqueLimit type-detection logic from Barplot_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. Replaces Order_Variables() and the Mod_Data <- dta[, Order_Var] pattern from tp.dp.EDA_barplots_scatterplots_varnames.R.
  • Added sample_eda_data() — mixed-type cardiac-surgery registry simulation (binary, ordinal, character-categorical, and continuous variables) for demonstrating eda_plot() and eda_select_vars().
  • Reorganized inst/: moved par_cst.xpt and npar_cst.xpt to inst/extdata/ (standard R package location for bundled data files); removed unreferenced presentation and test artifacts (*.pptx, *.pdf, *.sas scratch files).
  • Extended nonparametric_curve_plot() examples: added dual-Y-axis example (Example 10, \dontrun) using scale_y_continuous(sec.axis = ...); noted cll_p95/clu_p95 column availability for 95 % CI (Example 2) and per-group shape mapping via scale_shape_manual() (Example 4).
  • Extended nonparametric_ordinal_plot() examples: added pre-operative severity comparison example grouping combined Mild/Moderate/Severe cohorts through nonparametric_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 via save_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 %kaplan and %nelsont macros from tp.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() and sample_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 survival to package Imports.
  • 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 -> linewidth in element_line and element_rect).
  • Removed empty save.hvtiplotr function.
  • Fixed theme_dark_ppt to pass all parameters to theme_grey.
  • Updated documentation for data objects.
  • Updated README to reference officer package instead of deprecated ReporteRs.

hvtiPlotR 0.2.0

  • Initial CRAN submission.