tabular tabular website

CRAN status R-CMD-check Codecov test coverage Project Status: Active

tabular turns a pre-summarised data frame into a submission-grade clinical table and emits it natively to RTF, PDF, HTML, LaTeX, Typst, and DOCX — no Java, no LibreOffice, no Word automation. One short pipeline gives you decimal alignment via real font metrics, multi-level column headers, predicate-targeted styling, and group-aware pagination, built for CDISC ADaM workflows and FDA / EMA / PMDA submissions.

It is the only R table package that pairs a live HTML preview with a paginated print deliverable: the same spec you eyeball in a notebook is the one that paginates into the RTF you ship.

Scope. tabular renders the full set of clinical outputs – tables, listings, and figures (the “T”, “L”, and “F” of TFL) – to RTF, LaTeX, Typst, HTML, PDF, and DOCX from one verb pipeline. A zero-row table renders an empty-data placeholder (“No data available to report”) in the body, with the page chrome and column headers intact.

Installation

Install the released version from CRAN:

install.packages("tabular")

Or the development version from GitHub:

# install.packages("pak")
pak::pak("vthanik/tabular")
# or
remotes::install_github("vthanik/tabular")

R dependencies install automatically. The backends differ in what else they need:

Backend Extra requirement
RTF, DOCX, HTML, Markdown none — pure R, no Java, no pandoc, no Office
LaTeX (.tex source), Typst (.typ source) none — tabular writes the source
PDF one of two engines: a TeX install (xelatex), or a typst binary — Quarto ≥ 1.4 bundles one, so most machines already qualify

PDF is the only backend that shells out, and it has two engines:

Pass format = "latex" or format = "typst" to pick an engine explicitly; with neither engine present, install one:

install.packages("tinytex")
tinytex::install_tinytex(bundle = "TinyTeX") # one-time TeX setup
# or: install Quarto (https://quarto.org) — it bundles the typst engine

check_latex() reports which LaTeX packages resolve (probed through kpsewhich, the same resolver xelatex uses) and prints the remedy for anything genuinely missing; check_typst() does the same for the typst engine (binary, version floor, and the font chain PDFs render in); check_fonts(spec) audits the fonts a spec asks for, per backend.

tabular::check_latex()   # LaTeX-PDF readiness, with the install remedy
tabular::check_typst()   # Typst-PDF readiness (no TeX needed)

TeX Live on a managed OS. If TeX Live came from the system package manager (RHEL dnf, Debian/Ubuntu apt), its tlmgr is usually locked and tlmgr_install() fails on permissions. Install user-space TinyTeX alongside it rather than fighting the system copy — and never reach for --ignore-warning to force it.

A table in one pipeline

The pipeline starts from a pre-summarised wide data frame (one row in = one display row — tabular does no aggregation) and chains one verb per concern. Every verb returns an updated, immutable tabular_spec; the engine resolves it at render time.

library(tabular)

# BigN denominators, keyed by arm
n <- stats::setNames(cdisc_saf_n$n, cdisc_saf_n$arm_short)

# columns render in data-frame order, so put them in dose order first;
# subset to Age / Sex / Race for a compact display
keep <- c("Age (years)", "Sex, n (%)", "Race, n (%)")
demo <- cdisc_saf_demo[
  cdisc_saf_demo$variable %in% keep,
  c("variable", "stat_label", "placebo", "drug_50", "drug_100", "Total")
]

tab <- tabular(
  demo,
  titles = c(
    "Table 14.1.1",
    "Demographic and Baseline Characteristics",
    "Safety Population"
  ),
  footnotes = "Percentages are based on the number of subjects per treatment group."
) |>
  cols(
    variable = "Characteristic",
    stat_label = "Statistic",
    placebo = col_spec(
      label = "Placebo (N={n['placebo']})",
      align = "decimal"
    ),
    drug_50 = col_spec(
      label = "Drug 50 (N={n['drug_50']})",
      align = "decimal"
    ),
    drug_100 = col_spec(
      label = "Drug 100 (N={n['drug_100']})",
      align = "decimal"
    ),
    Total = col_spec(label = "Total (N={n['Total']})", align = "decimal")
  ) |>
  group_rows(by = "variable")

# render to any backend by file extension (or format = "...")
path <- emit(tab, tempfile(fileext = ".rtf")) # submission deliverable

The same tab emits to every backend from the one spec. The table below is tabular’s own HTML render — the identical spec also produces RTF, a paginated PDF (LaTeX- or typst-compiled), a tabularray LaTeX fragment, a native Typst document, and native OOXML .docx:

Demographic and baseline characteristics table rendered by tabular: decimal-aligned arm columns, a centred multi-line caption, and a single footnote.

Why tabular?

Where tabular fits

tabular is a renderer for pre-summarised clinical tables, not a statistics engine. Compute the summary upstream — with cards, gtsummary, dplyr, or SAS — then hand the finished wide frame to tabular(). Reach for gtsummary or rtables when you want the package to compute the summary; reach for tabular to render a summary you already have to submission-grade output.

The matrix reflects each package’s documented export surface (verified against their namespaces; via gt means gtsummary renders through gt):

tabular gt rtables gtsummary flextable huxtable
Computes statistics — — ✓ ✓ — —
Live HTML preview ✓ ✓ — ✓ ✓ ✓
Native RTF ✓ ✓ — via gt ✓ ✓
Native DOCX ✓ ✓ — via gt ✓ ✓
LaTeX ✓ ✓ — via gt — ✓
PDF ✓ ✓ ✓ via gt — ✓
Paginated submission output ✓ — ✓ — — —
Decimal align via font metrics ✓ — — — — —
CDISC ARS audit manifest ✓ — — — — —

Two notes on the marks:

Documentation

License

MIT © Vignesh Thanikachalam

mirror server hosted at Truenetwork, Russian Federation.