Base R Programming Skill
A comprehensive reference for base R programming — covering data structures, control flow, functions, I/O, statistical computing, and plotting.
Quick Reference
Data Structures
# Vectors (atomic)
x <- c(1, 2, 3) # numeric
y <- c("a", "b", "c") # character
z <- c(TRUE, FALSE, TRUE) # logical
# Factor
f <- factor(c("low", "med", "high"), levels = c("low", "med", "high"), ordered = TRUE)
# Matrix
m <- matrix(1:6, nrow = 2, ncol = 3)
m[1, ] # first row
m[, 2] # second column
# List
lst <- list(name = "ali", scores = c(90, 85), passed = TRUE)
lst$name # access by name
lst[[2]] # access by position
# Data frame
df <- data.frame(
id = 1:3,
name = c("a", "b", "c"),
value = c(10.5, 20.3, 30.1),
stringsAsFactors = FALSE
)
df[df$value > 15, ] # filter rows
df$new_col <- df$value * 2 # add column
Subsetting
# Vectors
x[1:3] # by position
x[c(TRUE, FALSE)] # by logical
x[x > 5] # by condition
x[-1] # exclude first
# Data frames
df[1:5, ] # first 5 rows
df[, c("name", "value")] # select columns
df[df$value > 10, "name"] # filter + select
subset(df, value > 10, select = c(name, value))
# which() for index positions
idx <- which(df$value == max(df$value))
Control Flow
# if/else
if (x > 0) {
"positive"
} else if (x == 0) {
"zero"
} else {
"negative"
}
# ifelse (vectorized)
ifelse(x > 0, "pos", "neg")
# for loop
for (i in seq_along(x)) {
cat(i, x[i], "\n")
}
# while
while (condition) {
# body
if (stop_cond) break
}
# switch
switch(type,
"a" = do_a(),
"b" = do_b(),
stop("Unknown type")
)
Functions
# Define
my_func <- function(x, y = 1, ...) {
result <- x + y
return(result) # or just: result
}
# Anonymous functions
sapply(1:5, function(x) x^2)
# R 4.1+ shorthand:
sapply(1:5, \(x) x^2)
# Useful: do.call for calling with a list of args
do.call(paste, list("a", "b", sep = "-"))
Apply Family
# sapply — simplify result to vector/matrix
sapply(lst, length)
# lapply — always returns list
lapply(lst, function(x) x[1])
# vapply — like sapply but with type safety
vapply(lst, length, integer(1))
# apply — over matrix margins (1=rows, 2=cols)
apply(m, 2, sum)
# tapply — apply by groups
tapply(df$value, df$group, mean)
# mapply — multivariate
mapply(function(x, y) x + y, 1:3, 4:6)
# aggregate — like tapply for data frames
aggregate(value ~ group, data = df, FUN = mean)
String Operations
paste("a", "b", sep = "-") # "a-b"
paste0("x", 1:3) # "x1" "x2" "x3"
sprintf("%.2f%%", 3.14159) # "3.14%"
nchar("hello") # 5
substr("hello", 1, 3) # "hel"
gsub("old", "new", text) # replace all
grep("pattern", x) # indices of matches
grepl("pattern", x) # logical vector
strsplit("a,b,c", ",") # list("a","b","c")
trimws(" hi ") # "hi"
tolower("ABC") # "abc"
Data I/O
# CSV
df <- read.csv("data.csv", stringsAsFactors = FALSE)
write.csv(df, "output.csv", row.names = FALSE)
# Tab-delimited
df <- read.delim("data.tsv")
# General
df <- read.table("data.txt", header = TRUE, sep = "\t")
# RDS (single R object, preserves types)
saveRDS(obj, "data.rds")
obj <- readRDS("data.rds")
# RData (multiple objects)
save(df1, df2, file = "data.RData")
load("data.RData")
# Connections
con <- file("big.csv", "r")
chunk <- readLines(con, n = 100)
close(con)
Base Plotting
# Scatter
plot(x, y, main = "Title", xlab = "X", ylab = "Y",
pch = 19, col = "steelblue", cex = 1.2)
# Line
plot(x, y, type = "l", lwd = 2, col = "red")
lines(x, y2, col = "blue", lty = 2) # add line
# Bar
barplot(table(df$category), main = "Counts",
col = "lightblue", las = 2)
# Histogram
hist(x, breaks = 30, col = "grey80",
main = "Distribution", xlab = "Value")
# Box plot
boxplot(value ~ group, data = df,
col = "lightyellow", main = "By Group")
# Multiple plots
par(mfrow = c(2, 2)) # 2x2 grid
# ... four plots ...
par(mfrow = c(1, 1)) # reset
# Save to file
png("plot.png", width = 800, height = 600)
plot(x, y)
dev.off()
# Add elements
legend("topright", legend = c("A", "B"),
col = c("red", "blue"), lty = 1)
abline(h = 0, lty = 2, col = "grey")
text(x, y, labels = names, pos = 3, cex = 0.8)
Statistics
# Descriptive
mean(x); median(x); sd(x); var(x)
quantile(x, probs = c(0.25, 0.5, 0.75))
summary(df)
cor(x, y)
table(df$category) # frequency table
# Linear model
fit <- lm(y ~ x1 + x2, data = df)
summary(fit)
coef(fit)
predict(fit, newdata = new_df)
confint(fit)
# t-test
t.test(x, y) # two-sample
t.test(x, mu = 0) # one-sample
t.test(before, after, paired = TRUE)
# Chi-square
chisq.test(table(df$a, df$b))
# ANOVA
fit <- aov(value ~ group, data = df)
summary(fit)
TukeyHSD(fit)
# Correlation test
cor.test(x, y, method = "pearson")
Data Manipulation
# Merge (join)
merged <- merge(df1, df2, by = "id") # inner
merged <- merge(df1, df2, by = "id", all = TRUE) # full outer
merged <- merge(df1, df2, by = "id", all.x = TRUE) # left
# Reshape
wide <- reshape(long, direction = "wide",
idvar = "id", timevar = "time", v.names = "value")
long <- reshape(wide, direction = "long",
varying = list(c("v1", "v2")), v.names = "value")
# Sort
df[order(df$value), ] # ascending
df[order(-df$value), ] # descending
df[order(df$group, -df$value), ] # multi-column
# Remove duplicates
df[!duplicated(df), ]
df[!duplicated(df$id), ]
# Stack / combine
rbind(df1, df2) # stack rows (same columns)
cbind(df1, df2) # bind columns (same rows)
# Transform columns
df$log_val <- log(df$value)
df$category <- cut(df$value, breaks = c(0, 10, 20, Inf),
labels = c("low", "med", "high"))
Environment & Debugging
ls() # list objects
rm(x) # remove object
rm(list = ls()) # clear all
str(obj) # structure
class(obj) # class
typeof(obj) # internal type
is.na(x) # check NA
complete.cases(df) # rows without NA
traceback() # after error
debug(my_func) # step through
browser() # breakpoint in code
system.time(expr) # timing
Sys.time() # current time
Reference Files
For deeper coverage, read the reference files in references/:
Function Gotchas & Quick Reference (condensed from R 4.5.3 Reference Manual)
Non-obvious behaviors, surprising defaults, and tricky interactions — only what Claude doesn't already know:
- data-wrangling.md — Read when: subsetting returns wrong type, apply on data frame gives unexpected coercion, merge/split/cbind behaves oddly, factor levels persist after filtering, table/duplicated edge cases.
- modeling.md — Read when: formula syntax is confusing (
I(), * vs :, /), aov gives wrong SS type, glm silently fits OLS, nls won't converge, predict returns wrong scale, optim/optimize needs tuning.
- statistics.md — Read when: hypothesis test gives surprising result, need to choose correct p.adjust method, clustering parameters seem wrong, distribution function naming is confusing (
d/p/q/r prefixes).
- visualization.md — Read when: par settings reset unexpectedly, layout/mfrow interaction is confusing, axis labels are clipped, colors don't look right, need specialty plots (contour, persp, mosaic, pairs).
- io-and-text.md — Read when: read.table silently drops data or misparses columns, regex behaves differently than expected, sprintf formatting is tricky, write.table output has unwanted row names.
- dates-and-system.md — Read when: Date/POSIXct conversion gives wrong day, time zones cause off-by-one, difftime units are unexpected, need to find/list/test files programmatically.
- misc-utilities.md — Read when: do.call behaves differently than direct call, need Reduce/Filter/Map, tryCatch handler doesn't fire, all.equal returns string not logical, time series functions need setup.
Tips for Writing Good R Code
- Use
vapply() over sapply() in production code — it enforces return types
- Prefer
seq_along(x) over 1:length(x) — the latter breaks when x is empty
- Use
stringsAsFactors = FALSE in read.csv() / data.frame() (default changed in R 4.0)
- Vectorize operations instead of writing loops when possible
- Use
stop(), warning(), message() for error handling — not print()
<<- assigns to parent environment — use sparingly and intentionally
with(df, expr) avoids repeating df$ everywhere
Sys.setenv() and .Renviron for environment variables
1---2name: base-r3description: Provides base R programming guidance covering data structures, data wrangling, statistical modeling, visualization, and I/O — using only packages included in a standard R installation. Use when the user writes or debugs R scripts, works with vectors, matrices, lists, data frames, or factors, applies the apply family (sapply, lapply, vapply, tapply), fits models with lm/glm/aov/nls, creates base graphics with plot/barplot/hist/boxplot/par, reads or writes CSV/RDS/RData files, or asks about R language semantics such as environments, scoping, and non-standard evaluation. Also use for R statistical functions (t.test, chisq.test, cor, kmeans, prcomp), string operations, date handling, and file system utilities. Do not use for tidyverse workflows (dplyr, tidyr, purrr), ggplot2, data.table, Shiny, R package development, Rcpp, or Python/Julia/Stata tasks.4---56# Base R Programming Skill78A comprehensive reference for base R programming — covering data structures, control flow, functions, I/O, statistical computing, and plotting.910## Quick Reference1112### Data Structures1314```r15# Vectors (atomic)16x <- c(1, 2, 3) # numeric17y <- c("a", "b", "c") # character18z <- c(TRUE, FALSE, TRUE) # logical1920# Factor21f <- factor(c("low", "med", "high"), levels = c("low", "med", "high"), ordered = TRUE)2223# Matrix24m <- matrix(1:6, nrow = 2, ncol = 3)25m[1, ] # first row26m[, 2] # second column2728# List29lst <- list(name = "ali", scores = c(90, 85), passed = TRUE)30lst$name # access by name31lst[[2]] # access by position3233# Data frame34df <- data.frame(35 id = 1:3,36 name = c("a", "b", "c"),37 value = c(10.5, 20.3, 30.1),38 stringsAsFactors = FALSE39)40df[df$value > 15, ] # filter rows41df$new_col <- df$value * 2 # add column42```4344### Subsetting4546```r47# Vectors48x[1:3] # by position49x[c(TRUE, FALSE)] # by logical50x[x > 5] # by condition51x[-1] # exclude first5253# Data frames54df[1:5, ] # first 5 rows55df[, c("name", "value")] # select columns56df[df$value > 10, "name"] # filter + select57subset(df, value > 10, select = c(name, value))5859# which() for index positions60idx <- which(df$value == max(df$value))61```6263### Control Flow6465```r66# if/else67if (x > 0) {68 "positive"69} else if (x == 0) {70 "zero"71} else {72 "negative"73}7475# ifelse (vectorized)76ifelse(x > 0, "pos", "neg")7778# for loop79for (i in seq_along(x)) {80 cat(i, x[i], "\n")81}8283# while84while (condition) {85 # body86 if (stop_cond) break87}8889# switch90switch(type,91 "a" = do_a(),92 "b" = do_b(),93 stop("Unknown type")94)95```9697### Functions9899```r100# Define101my_func <- function(x, y = 1, ...) {102 result <- x + y103 return(result) # or just: result104}105106# Anonymous functions107sapply(1:5, function(x) x^2)108# R 4.1+ shorthand:109sapply(1:5, \(x) x^2)110111# Useful: do.call for calling with a list of args112do.call(paste, list("a", "b", sep = "-"))113```114115### Apply Family116117```r118# sapply — simplify result to vector/matrix119sapply(lst, length)120121# lapply — always returns list122lapply(lst, function(x) x[1])123124# vapply — like sapply but with type safety125vapply(lst, length, integer(1))126127# apply — over matrix margins (1=rows, 2=cols)128apply(m, 2, sum)129130# tapply — apply by groups131tapply(df$value, df$group, mean)132133# mapply — multivariate134mapply(function(x, y) x + y, 1:3, 4:6)135136# aggregate — like tapply for data frames137aggregate(value ~ group, data = df, FUN = mean)138```139140### String Operations141142```r143paste("a", "b", sep = "-") # "a-b"144paste0("x", 1:3) # "x1" "x2" "x3"145sprintf("%.2f%%", 3.14159) # "3.14%"146nchar("hello") # 5147substr("hello", 1, 3) # "hel"148gsub("old", "new", text) # replace all149grep("pattern", x) # indices of matches150grepl("pattern", x) # logical vector151strsplit("a,b,c", ",") # list("a","b","c")152trimws(" hi ") # "hi"153tolower("ABC") # "abc"154```155156### Data I/O157158```r159# CSV160df <- read.csv("data.csv", stringsAsFactors = FALSE)161write.csv(df, "output.csv", row.names = FALSE)162163# Tab-delimited164df <- read.delim("data.tsv")165166# General167df <- read.table("data.txt", header = TRUE, sep = "\t")168169# RDS (single R object, preserves types)170saveRDS(obj, "data.rds")171obj <- readRDS("data.rds")172173# RData (multiple objects)174save(df1, df2, file = "data.RData")175load("data.RData")176177# Connections178con <- file("big.csv", "r")179chunk <- readLines(con, n = 100)180close(con)181```182183### Base Plotting184185```r186# Scatter187plot(x, y, main = "Title", xlab = "X", ylab = "Y",188 pch = 19, col = "steelblue", cex = 1.2)189190# Line191plot(x, y, type = "l", lwd = 2, col = "red")192lines(x, y2, col = "blue", lty = 2) # add line193194# Bar195barplot(table(df$category), main = "Counts",196 col = "lightblue", las = 2)197198# Histogram199hist(x, breaks = 30, col = "grey80",200 main = "Distribution", xlab = "Value")201202# Box plot203boxplot(value ~ group, data = df,204 col = "lightyellow", main = "By Group")205206# Multiple plots207par(mfrow = c(2, 2)) # 2x2 grid208# ... four plots ...209par(mfrow = c(1, 1)) # reset210211# Save to file212png("plot.png", width = 800, height = 600)213plot(x, y)214dev.off()215216# Add elements217legend("topright", legend = c("A", "B"),218 col = c("red", "blue"), lty = 1)219abline(h = 0, lty = 2, col = "grey")220text(x, y, labels = names, pos = 3, cex = 0.8)221```222223### Statistics224225```r226# Descriptive227mean(x); median(x); sd(x); var(x)228quantile(x, probs = c(0.25, 0.5, 0.75))229summary(df)230cor(x, y)231table(df$category) # frequency table232233# Linear model234fit <- lm(y ~ x1 + x2, data = df)235summary(fit)236coef(fit)237predict(fit, newdata = new_df)238confint(fit)239240# t-test241t.test(x, y) # two-sample242t.test(x, mu = 0) # one-sample243t.test(before, after, paired = TRUE)244245# Chi-square246chisq.test(table(df$a, df$b))247248# ANOVA249fit <- aov(value ~ group, data = df)250summary(fit)251TukeyHSD(fit)252253# Correlation test254cor.test(x, y, method = "pearson")255```256257### Data Manipulation258259```r260# Merge (join)261merged <- merge(df1, df2, by = "id") # inner262merged <- merge(df1, df2, by = "id", all = TRUE) # full outer263merged <- merge(df1, df2, by = "id", all.x = TRUE) # left264265# Reshape266wide <- reshape(long, direction = "wide",267 idvar = "id", timevar = "time", v.names = "value")268long <- reshape(wide, direction = "long",269 varying = list(c("v1", "v2")), v.names = "value")270271# Sort272df[order(df$value), ] # ascending273df[order(-df$value), ] # descending274df[order(df$group, -df$value), ] # multi-column275276# Remove duplicates277df[!duplicated(df), ]278df[!duplicated(df$id), ]279280# Stack / combine281rbind(df1, df2) # stack rows (same columns)282cbind(df1, df2) # bind columns (same rows)283284# Transform columns285df$log_val <- log(df$value)286df$category <- cut(df$value, breaks = c(0, 10, 20, Inf),287 labels = c("low", "med", "high"))288```289290### Environment & Debugging291292```r293ls() # list objects294rm(x) # remove object295rm(list = ls()) # clear all296str(obj) # structure297class(obj) # class298typeof(obj) # internal type299is.na(x) # check NA300complete.cases(df) # rows without NA301traceback() # after error302debug(my_func) # step through303browser() # breakpoint in code304system.time(expr) # timing305Sys.time() # current time306```307308## Reference Files309310For deeper coverage, read the reference files in `references/`:311312### Function Gotchas & Quick Reference (condensed from R 4.5.3 Reference Manual)313Non-obvious behaviors, surprising defaults, and tricky interactions — only what Claude doesn't already know:314- **data-wrangling.md** — Read when: subsetting returns wrong type, apply on data frame gives unexpected coercion, merge/split/cbind behaves oddly, factor levels persist after filtering, table/duplicated edge cases.315- **modeling.md** — Read when: formula syntax is confusing (`I()`, `*` vs `:`, `/`), aov gives wrong SS type, glm silently fits OLS, nls won't converge, predict returns wrong scale, optim/optimize needs tuning.316- **statistics.md** — Read when: hypothesis test gives surprising result, need to choose correct p.adjust method, clustering parameters seem wrong, distribution function naming is confusing (`d`/`p`/`q`/`r` prefixes).317- **visualization.md** — Read when: par settings reset unexpectedly, layout/mfrow interaction is confusing, axis labels are clipped, colors don't look right, need specialty plots (contour, persp, mosaic, pairs).318- **io-and-text.md** — Read when: read.table silently drops data or misparses columns, regex behaves differently than expected, sprintf formatting is tricky, write.table output has unwanted row names.319- **dates-and-system.md** — Read when: Date/POSIXct conversion gives wrong day, time zones cause off-by-one, difftime units are unexpected, need to find/list/test files programmatically.320- **misc-utilities.md** — Read when: do.call behaves differently than direct call, need Reduce/Filter/Map, tryCatch handler doesn't fire, all.equal returns string not logical, time series functions need setup.321322## Tips for Writing Good R Code323324- Use `vapply()` over `sapply()` in production code — it enforces return types325- Prefer `seq_along(x)` over `1:length(x)` — the latter breaks when `x` is empty326- Use `stringsAsFactors = FALSE` in `read.csv()` / `data.frame()` (default changed in R 4.0)327- Vectorize operations instead of writing loops when possible328- Use `stop()`, `warning()`, `message()` for error handling — not `print()`329- `<<-` assigns to parent environment — use sparingly and intentionally330- `with(df, expr)` avoids repeating `df$` everywhere331- `Sys.setenv()` and `.Renviron` for environment variables