Defines a section of a report by attaching content-generating code to an
analysis grid. A section bundles up to three kinds of content — text, a
table, and a figure — each produced by a function applied to the section's
gathered data. Sections accumulate: piping one add_section() into the next
builds a multi-section report grid.
Usage
add_section(
.report,
id = "",
title = "",
description = "",
report_data,
txt.fn = NULL,
tbl.fn = NULL,
fig.fn = NULL,
.by = NULL
)Arguments
- .report
A named analysis grid (e.g. an analyzed decision grid), or the result of a previous
add_section()call to append to.add_section()re-evaluates against the grid by name, so the grid must be a named object in the calling environment.- id
A short identifier for the section, used later to lay it out and assemble it (e.g.
"robustness"). Should be unique within a report.- title, description
Section heading text. Processed with
glue::glue(), so they may interpolate.bycolumns to vary per subsection.- report_data
A code expression — usually a
compose_view()call — applied to the grid to gather this section's data into a single data frame. This data frame is what each content function receives.- txt.fn, tbl.fn, fig.fn
Content functions, each taking the
report_datadata frame as its first argument and returning one rendered object:txt.fna text string,tbl.fna table (data.frame,gt, orflextable),fig.fna figure (ggplotorpatchwork). Must be written as a call (with parentheses), since the data is piped in. Any may beNULL; an omitted content type yields an empty slot and is skipped when the section is rendered.- .by
Columns that split the section into subsections, one per unique combination. When
NULL, the section is a single unit.
Value
A report grid (a tibble) with one row per subsection. Each row
carries the section's id, the per-subsection title and description, and the
content-generating code for each channel in the sec_txt, sec_tbl, and
sec_fig columns — full pipelines from the analysis grid through the
content function, stored as code and evaluated only when the section is
previewed or generated. Empty channels hold "NULL". The originating grid
name is recorded as an "analysis_grid" attribute so successive
add_section() calls can chain.
Details
add_section() records the content functions (txt.fn, tbl.fn, fig.fn)
as code; it does not run them. Realization is deferred to the moment a
section is previewed (preview_section()) or the document is generated
(generate_docs()), so defining a section is cheap no matter how many
subsections it fans out into. The stored code is the section — it is read
back when inspecting, and evaluated when rendering.
add_section() stores code, not rendered output. The sec_txt, sec_tbl,
and sec_fig columns each hold a complete pipeline — the analysis grid,
through report_data, through the content function — as an evaluable string.
Nothing is rendered here; realization happens downstream. This is the
package's code-as-artifact principle in its strongest form: a section is
fully described by readable, re-runnable code, inspectable with
show_section_content() and rendered only at preview or generation time.
Deferring realization also keeps add_section() fast: a section fanned out
into many subsections costs no rendering at definition time, since the
content is rendered later — once — when the document is built.
Content functions have access only to the gathered section data, not to the
section's metadata fields — a figure title, for instance, belongs inside
fig.fn, while a section- or slide-level heading belongs in title.
Section content
gather, then distil:
A section is built in two steps. First, report_data gathers the data the
section needs from the grid — typically a compose_view() call that joins
the relevant unpacked results into a single data frame. Second, each content
function (txt.fn, tbl.fn, fig.fn) receives that gathered data frame as
its first argument and distils it into one rendered object: a text string, a
table, or a figure.
So the flow is always: report_data produces the section's data frame, and
each content function consumes that same data frame. A content function is
written as code that takes the compose_view output as its first argument —
for example fig.fn = ggplot(aes(...)) + geom_point(), or a call to a named
function whose first argument is the data, written with parentheses as
fig.fn = make_spec_curve() so it sits correctly after the pipe. Because the
data is piped into the content function, the function must be written as a
call (with parentheses), not a bare name.
A section need not have all three content types. Any of txt.fn, tbl.fn,
or fig.fn may be left NULL, in which case that content slot is empty and
simply omitted wherever the section is rendered. A figure-only section, a
table-plus-text section, or any combination is valid.
Sections and subsections
When .by is supplied, the section is fanned out into one subsection per
unique combination of the .by columns. Each subsection receives its own
filtered slice of the grid and its own title and description. Because
title and description are processed with glue::glue(), they can
reference the .by columns to produce per-subsection labels — for example,
title = "Results for {outcome}" yields a distinct title per outcome.
Without .by, the section is a single unit with one title and description,
and glue interpolation of the .by columns does not apply (there are no
subsections to vary over).
See also
compose_view() for gathering results for section reporting;
show_section_content() to inspect one section's content code and render it;
preview_section() to preview a section's composed layout;
layout_section() and generate_docs() to assemble sections into a document.
Examples
if (FALSE) { # \dontrun{
# A figure-only section, fanned out by outcome
report <-
analyzed_grid |>
add_section(
id = "estimates",
title = "Effect estimates for {outcome}",
description = "Distribution across specifications",
report_data = compose_view(model_parameters, model_performance),
fig.fn = make_spec_curve(),
.by = outcome
)
# Chain a table-only section onto the same report
report <-
report |>
add_section(
id = "robustness",
title = "Robustness summary",
report_data = compose_view(rob = assess_robustness),
tbl.fn = gt::gt()
)
} # }