reporting/rmarkdown-reports/SKILL.md
Creates reproducible R Markdown analysis reports (HTML, PDF, Word) with knitr, covering the render pipeline, the interactive-vs-knit session trap, cache invalidation, bookdown cross-references, parameterization, and environment pinning. Use when generating an R-based analysis report, debugging a report that knits differently than it runs interactively, or fixing caching or cross-references.
npx skillsauth add GPTomics/bioSkills bio-reporting-rmarkdown-reportsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Reference examples tested with: rmarkdown 2.25+, knitr 1.45+, bookdown 0.37+, DESeq2 1.42+, ggplot2 3.5+, DT 0.31+, kableExtra 1.4+
Before using code patterns, verify installed versions match. If versions differ:
packageVersion('<pkg>') then ?function_name to verify parametersIf code throws an error, introspect the installed package (?rmarkdown::render, ?knitr::opts_chunk) and adapt the example to the actual API rather than retrying.
"Create an R Markdown report" -> Write an R-centric document combining code chunks, results, and narrative that knits to HTML/PDF/Word.
rmarkdown::render('report.Rmd'), or the Knit button in RStudioAn .Rmd always renders in two stages: knitr executes the chunks and weaves the results into an intermediate .md, then pandoc converts that .md into the target format (LaTeX via a TeX engine for PDF). knitr is the only execution engine for .Rmd (other languages run only as knitr engines); it is the successor to Sweave, adding caching, hooks, and markdown hosting. rmarkdown::render() orchestrates both stages. Knowing the split explains most failures: a chunk error is knitr; a formatting or cross-reference problem is usually pandoc/bookdown.
This is the single most common reproducibility surprise. rmarkdown::render() defaults to envir = parent.frame(), so calling it from the console evaluates chunks in the caller's environment - it can SEE objects sitting in the interactive global env. The RStudio Knit button does NOT: it spawns a fresh, clean R session. So a report that relies on a df created interactively renders fine via render() from the console, then fails when a colleague clicks Knit or CI runs it, because the fresh session has no df.
Guards:
rmarkdown::render('r.Rmd', envir = new.env()), or in a fresh process via callr::r(...) / xfun::Rscript_call(rmarkdown::render, ...).cache=TRUE stores a chunk's result in a *_cache/ dir and reloads it on re-knit if the chunk is "unchanged" - where the cache key is an MD5 of the chunk CODE plus evaluating options. The footgun: if a chunk reads data.csv and the FILE changes but the chunk code is byte-identical, the hash is unchanged and knitr serves the STALE cached result. Bind the data into the key:
```{r de-analysis, cache=TRUE, cache.extra=tools::md5sum('counts.csv')}
dds <- DESeq(DESeqDataSetFromMatrix(counts, metadata, ~ condition))
```
Cross-chunk dependencies are not tracked automatically either: if chunk B uses an object from chunk A, editing A does not invalidate B's cache by default - declare dependson='de-analysis' (or autodep=TRUE, best-effort).
knitr evaluates chunks with the working directory set to the directory of the .Rmd, NOT the project root. So read.csv('data/x.csv') works when run interactively from the project root but breaks on knit if the .Rmd lives in reports/. Fixes, in order of preference: here::here('data/x.csv') (anchors to the project root, most robust); knitr::opts_knit$set(root.dir = '...') in the setup chunk (note opts_knit, not opts_chunk); or rmarkdown::render('r.Rmd', knit_root_dir = '...'). Never setwd() in a chunk - it desyncs figure/cache file placement.
Base rmarkdown CANNOT cross-reference figures, tables, sections, or equations. Use a bookdown output format - bookdown::html_document2, bookdown::pdf_document2, bookdown::word_document2 - which add numbering and \@ref(type:label). Two hard requirements: the figure/table chunk must be LABELED, and it must have a CAPTION (fig.cap=); a captionless figure is emitted unnumbered and cannot be referenced.
output:
bookdown::html_document2:
toc: true
```{r volcano, fig.cap="Volcano plot of differential expression"}
plot(res$log2FoldChange, -log10(res$pvalue))
```
See Figure \@ref(fig:volcano).
(Quarto has native cross-references without bookdown - see reporting/quarto-reports.)
Declare defaults in YAML and read them as a read-only list:
params:
count_file: "counts.csv"
fdr_threshold: 0.05
counts <- read.csv(params$count_file)
```
Override per render and loop over samples:
rmarkdown::render('report.Rmd', params = list(count_file = 'sampleB.csv'),
output_file = 'sampleB_report.html')
rmarkdown::render(..., params = 'ask') launches the "Knit with Parameters" UI.
---
title: "RNA-seq Report"
date: "`r Sys.Date()`"
output:
html_document:
toc: true
toc_float: true
code_folding: hide
self_contained: true # base64-embed assets into one portable HTML
---
A setup chunk with knitr::opts_chunk$set(echo=TRUE, message=FALSE, warning=FALSE, fig.width=10) sets document-wide defaults. Section tabs use ## Results {.tabset}. Inline results splice with `r ...`. For tables: knitr::kable() + kableExtra for STATIC publication tables; DT::datatable() for INTERACTIVE HTML exploration - DT is a JavaScript widget, not for print/PDF, and it inflates the HTML (see reporting/publication-tables for the formatted-table decision). self_contained: true (default for html_document) embeds all assets into one portable file at a size cost; htmlwidgets get inlined too.
rmarkdown does not pin package versions or R itself. A report that knits perfectly today can change output next year when a dependency updates. The document gives byte-reproducible output only if code, data, AND versions are unchanged - and versions are not in the repo unless pinned. Add renv::snapshot() (renv.lock, commit it) for package pinning, and a container (Docker/Apptainer) when the OS, TeX, and pandoc must also be fixed. End the report with sessionInfo() / sessioninfo::session_info() - provenance for the reader, not a restore mechanism. Seed any stochastic step (set.seed).
| Symptom | Cause | Fix |
|---------|-------|-----|
| Renders from console, fails on Knit | render sees globals (parent.frame); Knit uses a fresh session | make every object chunk-created; test with envir=new.env() |
| Stale results after editing data | cache keys on code, not data | cache.extra=tools::md5sum('data.csv') |
| read.csv('data/..') fails on knit | working dir = .Rmd folder, not project root | here::here() or knit_root_dir= |
| \@ref(fig:x) shows as ?? | base rmarkdown can't cross-ref, or no caption/label | bookdown *_document2 + chunk label + fig.cap |
| Edited upstream chunk, downstream cache stale | dependencies not tracked | dependson= or autodep=TRUE |
| Report changes output months later | environment not pinned | renv.lock + container; seed RNGs |
| PDF knit fails | no LaTeX | tinytex::install_tinytex() |
development
Installs 425 bioinformatics skills covering sequence analysis, RNA-seq, single-cell, variant calling, metagenomics, structural biology, and 56 more categories. Use when setting up bioinformatics capabilities or when a bioinformatics task requires specialized skills not yet installed.
testing
Chains a somatic (tumor-normal) SNV/indel and structural-variant pipeline end to end with GATK Mutect2 (or Strelka2), wiring the somatic-specific machinery - panel-of-normals and gnomAD germline-resource priors, GetPileupSummaries/CalculateContamination, and LearnReadOrientationModel FFPE/oxoG orientation-bias filtering fed into FilterMutectCalls. Use when calling somatic mutations from a tumor-normal pair (or tumor-only with PoN caveats), deciding which artifact filter removes which class of false positive, reasoning about VAF/purity/ploidy and clonal-vs-subclonal detection, adding somatic SV/CNV or TMB/MSI/signatures, or routing variants to AMP/ASCO/CAP tier and oncogenicity interpretation (never germline ACMG).
development
End-to-end pooled and single-cell CRISPR screen analysis from FASTQ to hit genes. Orchestrates library design QC, guide counting, six-stage screen QC (plasmid Gini, replicate Pearson, CEGv2 PR-AUC, copy-number artifact), method-appropriate hit calling across MAGeCK RRA/MLE, BAGEL2, drugZ, JACKS, and Chronos, cancer-cell-line copy-number correction (CRISPRcleanR / Chronos), batch correction for multi-batch screens, and the specialized branches for combinatorial paralog screens, single-cell Perturb-seq, base-editor variant-function screens, prime-editor screens, and in vivo bottleneck-aware screens. Use when analyzing any pooled CRISPR screen end-to-end, matching the hit-calling method to the experimental design, integrating copy-number correction into the pipeline, or branching the workflow for single-cell, combinatorial, base-editor, prime-editor, or in vivo variants.
development
Transcribe DNA to RNA and translate to protein using Biopython, with NCBI codon-table selection, CDS validation, and six-frame ORF finding. Use when converting a CDS or ORF to its amino-acid sequence, selecting a non-standard (mitochondrial, bacterial, ciliate) genetic code, validating a coding sequence, or scanning all reading frames.