| name | foundations-r-fundamentals |
| description | Read/write R syntax for bioinformatics (vectors, data.frame, matrices, d/p/q/r distributions, DESeq2). Use when porting Python to R, debugging R from a paper/pipeline, or running DESeq2/edgeR/Seurat scripts. |
| tool_type | r |
| primary_tool | R |
R Fundamentals for Bioinformatics
When to Use
- Reading or adapting R code from a published pipeline, DESeq2/edgeR/Seurat vignette, or supplementary script
- Translating a Python data-analysis snippet into R (or vice versa) and need the syntax mapping
- Debugging an off-by-one or "wrong subset" bug caused by R's 1-based, exclude-on-negative indexing
- Setting up a minimal Bioconductor workflow (install, DESeqDataSet, filtering results)
- Choosing/running a basic statistical test in R (t-test, Wilcoxon, chi-squared, correlation)
Version Compatibility
R ≥ 4.2, Bioconductor ≥ 3.17 (BiocManager ≥ 1.30), DESeq2 ≥ 1.40, dplyr ≥ 1.1, ggplot2 ≥ 3.4. Base-R syntax here (<-, data.frame, apply) is stable across R versions back to R 3.x.
Prerequisites
- R installed (
R --version), or an R kernel (IRkernel) for Jupyter
install.packages("BiocManager") for any Bioconductor package (DESeq2, GenomicRanges, ...)
- Comfort with Python's data types helps — this skill is written as a Python→R translation
Python vs R Syntax Traps
Goal: avoid the bugs that come from assuming R behaves like Python.
Approach: memorize this table before writing/reading any R code — every row is a real, common bug source when porting between the two languages.
| Feature | Python | R |
|---|
| Indexing | Starts at 0 | Starts at 1 |
| Assignment | = | <- (preferred; = works but differs inside function calls) |
| Negative index | x[-1] = last element | x[-1] = all except first |
| Boolean values | True / False | TRUE / FALSE |
| Missing data | None | NA |
| Null | None | NULL |
| Auto-print | print(x) | Just type x (top level only) |
| AND/OR | &, | (vectorized) | &, | (vectorized); &&, || (scalar, first element only) |
Vectors and Data Frames
Goal: manipulate vectors and data frames the R way.
Approach: use vectorized operations and logical indexing instead of loops; use data.frame for tabular data and filter with boolean masks on columns.
gene_expression <- c(2.5, 3.1, 4.2, 1.8, 5.6)
gene_names <- c("BRCA1", "TP53", "EGFR", "MYC", "KRAS")
positions <- 1:10
coverage <- seq(from = 0, to = 100, by = 10)
groups <- rep(c("control", "treatment"), each = 3)
gc_content <- c(BRCA1 = TP53 EGFR MYC
gc_rich gc_contentgc_content
log2fc log2gene_expression
pvalue
sig_up log2fc pvalue
cat gene_namessig_up
whichsig_up
gene_data data.frame
gene
log2fc
pvalue
stringsAsFactors
gene_datagene
gene_data
gene_data
gene_datagene_datalog2fc gene_datapvalue
gene_datapadj p.adjustgene_datapvalue method
gene_dataordergene_datapvalue
Matrices and Statistical Distributions
Goal: work with expression matrices and distribution functions.
Approach: use matrix() with dimnames for gene-by-sample data and apply() for row/column stats; use the d/p/q/r prefix family for any distribution.
expr_matrix <- matrix(
c(5.2, 3.1, 8.5, 6.2, 4.8, 2.9, 7.1, 5.8),
nrow = 2, byrow = TRUE,
dimnames = list(c("Sample1", "Sample2"), c("BRCA1", "TP53", "EGFR", "MYC"))
)
expr_matrix[2, "EGFR"]
expr_matrix[1
expr_matrix
applyexpr_matrix mean
applyexpr_matrix sd
set.seed
rnormn mean sd
pnormq lower.tail
qnormp
rnbinomn mu size
control rnorm mean sd
treatment rnorm mean sd
t.testcontrol treatmentp.value
wilcox.testcontrol treatmentp.value
chisq.testtable
Minimal DESeq2 Workflow
Goal: run a minimal DESeq2 differential-expression analysis.
Approach: build a DESeqDataSet from a raw count matrix + sample metadata, run DESeq(), extract a contrast, then filter by FDR and effect size.
if (!requireNamespace("BiocManager", quietly = TRUE)) install.packages("BiocManager")
BiocManager::install("DESeq2")
library(DESeq2)
run_deseq2 <- function(counts, metadata, alpha = 0.05, lfc = 1) {
dds <- DESeqDataSetFromMatrix(countData = counts, colData metadata design condition
dds DESeqdds
res resultsdds contrast
res_df as.data.frameres
sig res_dfres_dfpadj res_dfpadj alpha res_dflog2FoldChange lfc
sigordersigpadj
df read.csv stringsAsFactors
df read.delim sep
write.csvdf row.names
demo_deseq2 <- function() {
set.seed(1)
counts <- matrix(rnbinom(400, mu = 100, size = 5), nrow = 100, ncol = 4,
dimnames = list(paste0("gene", 1:100), paste0("s", 1:4)))
metadata <- data.frame(condition = factor(c("control", "control",
row.names colnamescounts
sig run_deseq2counts metadata
stopifnotis.data.framesig
stopifnot colnamessig
cat nrowsig nrowcounts
demo_deseq2
Pitfalls
- R indexing starts at 1:
x[1] is first; x[-1] means "all except first" (NOT last element like Python)
- Factors look like strings but are not: a factor stores integer codes + labels; functions expecting strings will misbehave — use
stringsAsFactors = FALSE in data.frame() or as.character() to convert
library() vs require(): use library() in scripts — it errors loudly if a package is missing; require() returns FALSE silently and can hide broken pipelines
- Data frames vs matrices: Bioconductor functions often require numeric matrices; a data frame with character columns cannot be used directly — convert with
as.matrix(df[, numeric_cols])
<- inside function arguments: f(x <- 1) assigns x in the enclosing scope AND passes the value as the argument; use = for keyword arguments to avoid the side effect
- Multiple testing:
p.adjust(..., method = "BH") (Benjamini-Hochberg FDR) is the genomics standard; Bonferroni is overly conservative for large gene sets
- Scalar vs vectorized AND/OR:
&& and || only evaluate the first element — always use & and | when filtering vectors/data frames
See Also
foundations-r-hypothesis-testing-and-nonparametrics — Shapiro-Wilk, Wilcoxon, ANOVA, correlation tests in depth
foundations-r-regression-correlation-and-diagnostics — linear models, lm(), diagnostic plots
foundations-biostatistics-fundamentals — statistical concepts underlying these tests
bio-differential-expression-deseq2-basics — full DESeq2 workflow beyond this quick-start