Базовый R
Предоставляет рекомендации по программированию на базовом R, охватывающие структуры данных, обработку данных, статистическое моделирование, визуализацию и ввод-вывод, с использованием только пакетов, включённых в стандартную установку R
В навыке 12 файлов. Их можно скопировать или скачать по отдельности, сохранив указанные имена и папки.
SKILL.md
---
name: base-r
description: Предоставляет рекомендации по программированию на базовом R, охватывающие структуры данных, обработку данных, статистическое моделирование, визуализацию и ввод-вывод, с использованием только пакетов, включённых в стандартную установку R
---
# Навык программирования на базовом R
Полный справочник по программированию на базовом R — структуры данных, управление потоком выполнения, функции, ввод-вывод, статистические вычисления и построение графиков.
## Краткий справочник
### Структуры данных
```r
# 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
```
### Выбор подмножеств
```r
# 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))
```
### Управление потоком выполнения
```r
# 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")
)
```
### Функции
```r
# 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
```r
# 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)
```
### Операции со строками
```r
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"
```
### Ввод-вывод данных
```r
# 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)
```
### Базовое построение графиков
```r
# 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)
```
### Статистика
```r
# 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")
```
### Преобразование данных
```r
# 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"))
```
### Окружение и отладка
```r
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
```
## Справочные файлы
Для более глубокого изучения читайте справочные файлы в `references/`:
### Подводные камни функций и краткий справочник (сокращённое изложение справочного руководства R 4.5.3)
Неочевидное поведение, неожиданные значения по умолчанию и сложные взаимодействия — только то, чего Claude ещё не знает:
- **data-wrangling.md** — Читать, когда: выбор подмножества возвращает неверный тип, apply для фрейма данных вызывает неожиданное приведение типов, merge/split/cbind ведут себя необычно, уровни факторов сохраняются после фильтрации, возникают крайние случаи table/duplicated.
- **modeling.md** — Читать, когда: непонятен синтаксис формул (`I()`, `*` в сравнении с `:`, `/`), aov выдаёт неверный тип SS, glm незаметно строит модель OLS, nls не сходится, predict возвращает неверную шкалу, optim/optimize требует настройки.
- **statistics.md** — Читать, когда: проверка гипотез даёт неожиданный результат, нужно выбрать правильный метод p.adjust, параметры кластеризации кажутся неверными, непонятны имена функций распределений (префиксы `d`/`p`/`q`/`r`).
- **visualization.md** — Читать, когда: настройки par неожиданно сбрасываются, непонятно взаимодействие layout/mfrow, подписи осей обрезаются, цвета выглядят неправильно, нужны специальные графики (contour, persp, mosaic, pairs).
- **io-and-text.md** — Читать, когда: read.table незаметно отбрасывает данные или неправильно разбирает столбцы, регулярные выражения ведут себя не так, как ожидалось, форматирование sprintf вызывает сложности, вывод write.table содержит нежелательные имена строк.
- **dates-and-system.md** — Читать, когда: преобразование Date/POSIXct даёт неправильный день, часовые пояса вызывают сдвиг на единицу, единицы difftime неожиданны, нужно программно искать/перечислять/проверять файлы.
- **misc-utilities.md** — Читать, когда: do.call ведёт себя иначе, чем прямой вызов, нужны Reduce/Filter/Map, обработчик tryCatch не срабатывает, all.equal возвращает строку вместо логического значения, требуется настройка функций временных рядов.
## Советы по написанию хорошего кода R
- Используйте `vapply()` вместо `sapply()` в промышленном коде — она обеспечивает соблюдение типов возвращаемых значений
- Предпочитайте `seq_along(x)` вместо `1:length(x)` — последний вариант ломается при пустом `x`
- Используйте `stringsAsFactors = FALSE` в `read.csv()` / `data.frame()` (значение по умолчанию изменилось в R 4.0)
- По возможности векторизуйте операции вместо написания циклов
- Используйте `stop()`, `warning()`, `message()` для обработки ошибок — не `print()`
- `<<-` выполняет присваивание в родительском окружении — используйте редко и осознанно
- `with(df, expr)` позволяет не повторять повсюду `df$`
- `Sys.setenv()` и `.Renviron` — для переменных окружения
references/misc-utilities.md
# Разные утилиты — краткий справочник
> Неочевидное поведение, подводные камни и непростые значения по умолчанию функций R.
> Только то, чего Claude ещё не знает.
---
## do.call
- `do.call(fun, args_list)` — `args` должен быть **списком**, даже для одного аргумента.
- `quote = TRUE` предотвращает вычисление аргументов перед вызовом — необходимо при передаче выражений/символов.
- Поведение `substitute` внутри `do.call` отличается от прямых вызовов. Семантика для этого случая определена не полностью.
- Полезный шаблон: `do.call(rbind, list_of_dfs)` для объединения списка фреймов данных.
---
## Reduce / Filter / Map / Find / Position
Вспомогательные функции функционального программирования из базового R — действительно неочевидные.
- `Reduce(f, x)` применяет бинарную функцию `f` накопительно: `Reduce("+", 1:4)` = `((1+2)+3)+4`. Направление важно для некоммутативных операций.
- `Reduce(f, x, accumulate = TRUE)` возвращает все промежуточные результаты — эквивалент Python `itertools.accumulate`.
- `Reduce(f, x, right = TRUE)` выполняет свёртку справа: `f(x1, f(x2, f(x3, x4)))`.
- `Reduce` с `init` добавляет начальное значение: `Reduce(f, x, init = v)` = `f(f(f(v, x1), x2), x3)`.
- `Filter(f, x)` сохраняет элементы, для которых `f(elem)` равно `TRUE`. В отличие от `x[sapply(x, f)]`, корректно обрабатывает `NULL`/пустые значения.
- `Map(f, ...)` — простая обёртка над `mapply(f, ..., SIMPLIFY = FALSE)` — всегда возвращает список.
- `Find(f, x)` возвращает **первый** элемент, для которого `f(elem)` равно `TRUE`. `Find(f, x, right = TRUE)` — для последнего.
- `Position(f, x)` возвращает **индекс** первого совпадения (как `Find`, но возвращает позицию, а не значение).
---
## lengths
- `lengths(x)` возвращает длину **каждого элемента** списка. Эквивалентна `sapply(x, length)`, но быстрее (реализована на C).
- Работает с любым спископодобным объектом. Возвращает целочисленный вектор.
---
## conditions (tryCatch / withCallingHandlers)
- `tryCatch` **разматывает** стек вызовов — обработчик выполняется в вызывающем окружении, а не там, где возникла ошибка. Возобновить выполнение невозможно.
- `withCallingHandlers` НЕ разматывает стек — обработчик выполняется там, где было сигнализировано условие. Можно исследовать/записать его в журнал, а затем позволить условию распространяться дальше.
- `tryCatch(expr, error = function(e) e)` возвращает объект условия ошибки.
- `tryCatch(expr, warning = function(w) {...})` перехватывает **первое** предупреждение и завершает выполнение. Используйте `withCallingHandlers` + `invokeRestart("muffleWarning")`, чтобы подавить предупреждения, но продолжить работу.
- Блок `finally` в `tryCatch` выполняется всегда (как try/finally в Java).
- `globalCallingHandlers()` регистрирует обработчики, действующие в течение сессии (полезно для журналирования).
- Пользовательские условия: `stop(errorCondition("msg", class = "myError"))`, затем перехват через `tryCatch(..., myError = function(e) ...)`.
---
## all.equal
- Проверяет **приблизительное равенство** с допуском (по умолчанию `1.5e-8`, то есть `sqrt(.Machine$double.eps)`).
- Возвращает `TRUE` или **символьную строку**, описывающую различие, — НЕ `FALSE`. В условиях используйте `isTRUE(all.equal(x, y))`.
- Аргумент `tolerance` управляет числовым допуском. `scale` — абсолютным или относительным сравнением.
- Проверяет атрибуты, имена, размерности — тщательнее, чем `==`.
---
## combn
- `combn(n, m)` или `combn(x, m)`: генерирует все сочетания из `m` элементов `x`.
- Возвращает **матрицу** с `m` строками; каждый столбец — одно сочетание.
- Аргумент `FUN` применяет функцию к каждому сочетанию: `combn(5, 3, sum)` возвращает суммы всех подмножеств из 3 элементов.
- `simplify = FALSE` возвращает список вместо матрицы.
---
## modifyList
- `modifyList(x, val)` заменяет элементы списка `x` элементами из `val` по **имени**.
- Установка значения `NULL` **удаляет** соответствующий элемент из списка.
- **Действительно** добавляет новые имена, отсутствующие в `x`, — внутри используется `x[names(val)] <- val`, поэтому любое имя из `val` добавляется или заменяется.
---
## relist
- Обратная операция к `unlist`: по плоскому вектору и списку-скелету восстанавливает вложенную структуру.
- `relist(flesh, skeleton)` — `flesh` содержит плоские данные, `skeleton` задаёт форму.
- Работает с факторами, матрицами и вложенными списками.
---
## txtProgressBar
- `txtProgressBar(min, max, style = 3)` — стиль 3 показывает процент + полосу (самый полезный).
- Обновление через `setTxtProgressBar(pb, value)`. Закрытие через `close(pb)`.
- Стиль 1: вращающийся `|/-\`, стиль 2: простой индикатор прогресса. Только стиль 3 показывает процент.
---
## object.size
- Возвращает **оценку** памяти, занимаемой объектом. Не всегда точна при общих ссылках.
- `format(object.size(x), units = "MB")` — для человекочитаемого вывода.
- Не учитывает размер окружений или внешних указателей.
---
## installed.packages / update.packages
- `installed.packages()` может работать медленно (сканирует все пакеты). Используйте `find.package()` или `requireNamespace()` для проверки конкретного пакета.
- `update.packages(ask = FALSE)` обновляет все пакеты без запросов.
- `lib.loc` указывает, какую библиотеку проверять/обновлять.
---
## vignette / demo
- `vignette()` перечисляет все виньетки; `vignette("name", package = "pkg")` открывает конкретную.
- `demo()` перечисляет все демонстрации; `demo("topic")` запускает одну интерактивно.
- `browseVignettes()` открывает HTML-браузер виньеток.
---
## Временные ряды: acf / arima / ts / stl / decompose
- `ts(data, start, frequency)`: `frequency` — число наблюдений на единицу времени (12 для ежемесячных, 4 для ежеквартальных).
- Для `acf` по умолчанию `type = "correlation"`. Используйте `type = "partial"` для PACF. `plot = FALSE` подавляет автоматическое построение графика.
- `arima(x, order = c(p,d,q))` — для моделей ARIMA. `seasonal = list(order = c(P,D,Q), period = S)` — для сезонной компоненты.
- `arima` обрабатывает значения `NA` во временном ряду (через фильтр Калмана).
- `stl` требует `s.window` (сезонное окно) — его нужно указать, значения по умолчанию нет. `s.window = "periodic"` предполагает фиксированную сезонность.
- `decompose`: проще, чем `stl`, использует скользящие средние. `type = "additive"` или `"multiplicative"`.
- Компоненты результата `stl`: матрица `$time.series` со столбцами `seasonal`, `trend`, `remainder`.
references/data-wrangling.md
# Обработка данных — краткий справочник > Неочевидное поведение, подводные камни и непростые значения по умолчанию функций R. > Только то, чего Claude ещё не знает. --- ## Extract / Extract.data.frame Подводные камни индексации в базовом R. - `m[j = 2, i = 1]` — это `m[2, 1]`, а не `m[1, 2]`: имена аргументов **игнорируются** в `[`, сопоставление только позиционное. Никогда не именуйте аргументы индексов. - Индексация фактором: `x[f]` использует целочисленные коды фактора `f`, а не его символьные метки. Используйте `x[as.character(f)]` для индексации по меткам. - `x[[]]` без индекса всегда вызывает ошибку. `x$name` по умолчанию выполняет частичное сопоставление; `x[["name"]]` — нет (по умолчанию точное). - Присваивание `NULL` через `x[[i]] <- NULL` или `x$name <- NULL` **удаляет** этот элемент списка. - `[` для фрейма данных с одним столбцом: `df[, 1]` возвращает **вектор** (по умолчанию drop=TRUE для столбцов), но `df[1, ]` возвращает **фрейм данных** (drop=FALSE для строк). Явно задавайте `drop = FALSE`. - Индексация фрейма данных матрицей (`df[cbind(i,j)]`) сначала преобразует его в матрицу — избегайте этого. --- ## subset Используйте только интерактивно; небезопасна для программирования. - Аргумент `subset` использует **нестандартное вычисление** — имена столбцов разрешаются во фрейме данных, что при программном использовании может незаметно подхватить не те переменные. В функциях используйте `[` с явной логикой. - `NA` в логическом условии трактуются как `FALSE` (строки незаметно отбрасываются). - После выбора подмножества факторы могут сохранять неиспользуемые уровни; вызывайте `droplevels()`. --- ## match / %in% - `%in%` **никогда не возвращает NA** — поэтому, в отличие от `==`, безопасен для условий `if()`. - `match()` возвращает только позицию **первого** совпадения; повторы в `table` игнорируются. - Факторы, векторы raw и списки перед сопоставлением преобразуются в символьный вид. - `NaN` совпадает с `NaN`, но не с `NA`; `NA` совпадает только с `NA`. --- ## apply - Для **фрейма данных** `apply` сначала выполняет преобразование в матрицу через `as.matrix` — смешанные типы становятся символьными. - Ориентация возвращаемого значения транспонирована: если FUN возвращает вектор длины n, размерность результата — `c(n, dim(X)[MARGIN])`. Результаты для строк становятся **столбцами**. - Результаты-факторы в выходном массиве преобразуются в символьные значения. - Имена аргументов `...` не должны совпадать с `X`, `MARGIN` или `FUN` (риск частичного сопоставления). --- ## lapply / sapply / vapply - `sapply` может непредсказуемо вернуть вектор, матрицу или список — в неинтерактивном коде используйте `vapply` с явным шаблоном `FUN.VALUE`. - Прямой вызов примитивов в `lapply` может вызвать проблемы диспетчеризации; оборачивайте в `function(x) is.numeric(x)`, а не передавайте просто `is.numeric`. - `sapply` с `simplify = "array"` может создавать массивы большей размерности (не только матрицы). --- ## tapply - Возвращает **массив** (не фрейм данных). Информация о классе возвращаемых значений **отбрасывается** (например, объекты Date становятся числовыми). - Аргументы `...` для FUN **не** разделяются по ячейкам — они применяются глобально, поэтому FUN не должна ожидать дополнительные аргументы той же длины, что и X. - `default = NA` заполняет пустые ячейки; задавайте `default = 0` для операций наподобие суммирования. До R 3.4.0 значение было жёстко задано как `NA`. - Используйте `array2DF()` для преобразования результата во фрейм данных. --- ## mapply - Имя аргумента — `SIMPLIFY` (все буквы заглавные), а не `simplify` — несогласованность с `sapply`. - `MoreArgs` должен быть **списком** аргументов, по которым не выполняется векторизация. - Повторяет более короткие аргументы до общей длины; аргумент нулевой длины даёт результат нулевой длины. --- ## merge - По умолчанию `by` равен `intersect(names(x), names(y))` — объединение может незаметно выполняться по непредусмотренным столбцам, если во фреймах данных совпадают имена столбцов. - `by = 0` или `by = "row.names"` объединяет по именам строк, добавляя столбец "Row.names". - `by = NULL` (или оба `by.x`/`by.y` длины 0) создаёт **декартово произведение**. - Результат по умолчанию сортируется по столбцам `by` (`sort = TRUE`). Для несортированного вывода используйте `sort = FALSE`. - Совпадения повторяющихся ключей создают **все комбинации** (одна строка на каждую пару совпадений). --- ## split - Если `f` — список факторов, используется их взаимодействие; уровни, содержащие `"."`, могут привести к неожиданным разбиениям, если не изменить `sep`. - `drop = FALSE` (по умолчанию) сохраняет пустые уровни факторов как пустые элементы списка. - Поддерживает синтаксис формул: `split(df, ~ Month)`. --- ## cbind / rbind - `cbind` для фреймов данных вызывает `data.frame(...)`, а не `cbind.matrix`. Смешивание матриц и фреймов данных может дать неожиданные результаты. - `rbind` для фреймов данных сопоставляет столбцы **по имени**, а не по позиции. Отсутствующие столбцы получают `NA`. - `cbind(NULL)` возвращает `NULL` (не матрицу). Для согласованности `rbind(NULL)` также возвращает `NULL`. --- ## table - По умолчанию **исключает NA** (`useNA = "no"`). Используйте `useNA = "ifany"` или `exclude = NULL`, чтобы подсчитывать NA. - Непустое и не стандартное значение `exclude` подразумевает `useNA = "ifany"`. - Результат всегда является **массивом** (даже одномерным), класса "table". Преобразуйте во фрейм данных через `as.data.frame(tbl)`. - Два вида NA (NA как уровень фактора и фактический NA) обрабатываются по-разному в зависимости от `useNA`/`exclude`. --- ## duplicated / unique - `duplicated` помечает **второе и последующие** вхождения как TRUE, не первое. Используйте `fromLast = TRUE` для обратного порядка. - Для фреймов данных работает с целыми строками. Для списков выполняет рекурсивное сравнение. - `unique` сохраняет **первое** вхождение каждого значения. --- ## data.frame (подводные камни) - `stringsAsFactors = FALSE` — значение по умолчанию начиная с R 4.0.0 (раньше было TRUE). - Атомарные векторы повторяются до длины самого длинного столбца, но только при точной кратности. Защитите их с помощью `I()`, чтобы предотвратить преобразование. - Повторяющиеся имена столбцов допускаются только с `check.names = FALSE`, но многие операции незаметно устраняют повторы имён. - Аргументы-матрицы разворачиваются в несколько столбцов, если не защищены `I()`. --- ## factor (подводные камни) - `as.numeric(f)` возвращает **целочисленные коды**, а не исходные значения. Используйте `as.numeric(levels(f))[f]` или `as.numeric(as.character(f))`. - Между факторами работают только `==` и `!=`; у факторов должны быть одинаковые наборы уровней. Упорядоченные факторы поддерживают `<`, `>`. - `c()` для факторов объединяет наборы уровней (начиная с R 4.1.0), но более ранние версии преобразовывали их в целые числа. - Уровни по умолчанию сортируются, но порядок сортировки **зависит от локали** на момент создания. --- ## aggregate - Интерфейс формул (`aggregate(y ~ x, data, FUN)`) по умолчанию отбрасывает группы `NA`. - Метод для фрейма данных требует, чтобы `by` был **списком** (не вектором). - Возвращает столбцы, названные по группирующим переменным, а столбец результата сохраняет исходное имя. - Если FUN возвращает несколько значений, столбец результата является **столбцом-матрицей** внутри фрейма данных. --- ## complete.cases - Возвращает логический вектор: TRUE для строк, в которых **нет** NA во всех столбцах/аргументах. - Работает с несколькими аргументами (например, `complete.cases(x, y)` проверяет оба). --- ## order - Возвращает **вектор перестановки** индексов, а не отсортированные значения. Используйте `x[order(x)]` для сортировки. - По умолчанию сортирует по возрастанию; используйте `-x` для числовой сортировки по убыванию или `decreasing = TRUE`. - Сортировка символов зависит от локали. Используйте `method = "radix"` для быстрой сортировки, не зависящей от локали. - `sort.int()` с `method = "radix"` значительно быстрее для больших целочисленных/символьных векторов.
references/dates-and-system.md
# Даты и система — краткий справочник
> Неочевидное поведение, подводные камни и непростые значения по умолчанию функций R.
> Только то, чего Claude ещё не знает.
---
## Dates (класс Date)
- Объекты `Date` хранятся как **целое число дней с 1970-01-01**. Арифметика выполняется в днях.
- `Sys.Date()` возвращает текущую дату как объект Date.
- `seq.Date(from, to, by = "month")` — приращения "month" могут создавать интервалы разной длины. Прибавление 1 месяца к 31 января даёт 3 марта (не 28 февраля).
- `diff(dates)` возвращает объект `difftime` в днях.
- `format(date, "%Y")` — год, `"%m"` — месяц, `"%d"` — день, `"%A"` — название дня недели (зависит от локали).
- Годы до 1 года н. э. могут обрабатываться некорректно.
- `length(date_vector) <- n` при увеличении длины дополняет вектор значениями `NA`.
---
## DateTimeClasses (POSIXct / POSIXlt)
- `POSIXct`: секунды с 1970-01-01 UTC (компактный числовой вектор).
- `POSIXlt`: список с компонентами `$sec`, `$min`, `$hour`, `$mday`, `$mon` (0–11!), `$year` (с 1900 года!), `$wday` (0–6, воскресенье=0), `$yday` (0–365).
- Преобразование между POSIXct и Date: `as.Date(posixct_obj)` по умолчанию использует `tz = "UTC"` — может дать не ту дату, которую предполагали, если оригинал был в другом часовом поясе.
- `Sys.time()` возвращает POSIXct в текущем часовом поясе.
- `strptime` возвращает POSIXlt; для получения POSIXct используйте `as.POSIXct(strptime(...))`.
- Арифметика `difftime`: вычитание объектов POSIXct даёт difftime. Единицы выбираются автоматически ("secs", "mins", "hours", "days", "weeks").
---
## difftime
- `difftime(time1, time2, units = "auto")` — автоматически выбирает наименьшую разумную единицу измерения.
- Явные единицы: `"secs"`, `"mins"`, `"hours"`, `"days"`, `"weeks"`. Нет "months" или "years" (переменная длительность).
- `as.numeric(diff, units = "hours")` извлекает числовое значение в заданных единицах.
- `units(diff_obj) <- "hours"` меняет единицу непосредственно в объекте.
---
## system.time / proc.time
- `system.time(expr)` возвращает время `user`, `system` и `elapsed`.
- `gcFirst = TRUE` (по умолчанию): запускает сборку мусора перед замером для большей согласованности результатов.
- `proc.time()` возвращает накопленное время с запуска R — для интервалов вычисляйте разности.
- `elapsed` (реальное время по часам) может быть меньше `user` (многопоточный BLAS) или больше (ожидание ввода-вывода).
---
## Sys.sleep
- `Sys.sleep(seconds)` — допускает дробные секунды. Фактическое ожидание может оказаться дольше (планирование ОС).
- Во время ожидания процесс **уступает управление** ОС (не выполняет активное ожидание).
---
## options (ключевые параметры)
Избранные неочевидные параметры:
- `options(scipen = n)`: положительное значение смещает выбор к фиксированной записи, отрицательное — к научной. По умолчанию 0. Применяется к `print`/`format`/`cat`, но не к `sprintf`.
- `options(digits = n)`: число значащих цифр при печати (1–22, по умолчанию 7). Только рекомендация.
- `options(digits.secs = n)`: максимальное число десятичных знаков для секунд при форматировании времени (0–6, по умолчанию 0).
- `options(warn = n)`: -1 = игнорировать предупреждения, 0 = собирать (по умолчанию), 1 = выводить сразу, 2 = преобразовывать в ошибки.
- `options(error = recover)`: переход в отладчик при ошибке. `options(error = NULL)` сбрасывает к значению по умолчанию.
- `options(OutDec = ",")`: меняет десятичный разделитель в выводе (влияет на `format`, `print`, НЕ на `sprintf`).
- `options(stringsAsFactors = FALSE)`: глобальное значение по умолчанию для `data.frame` (неактуально с R 4.0.0, где оно уже FALSE).
- `options(expressions = 5000)`: максимальное число вложенных вычислений. Увеличивайте для глубокой рекурсии.
- `options(max.print = 99999)`: управляет усечением вывода `print`.
- `options(na.action = "na.omit")`: обработка NA по умолчанию в функциях моделирования.
- `options(contrasts = c("contr.treatment", "contr.poly"))`: контрасты по умолчанию для неупорядоченных/упорядоченных факторов.
---
## file.path / basename / dirname
- `file.path("a", "b", "c.txt")` → `"a/b/c.txt"` (разделитель, соответствующий платформе).
- `basename("/a/b/c.txt")` → `"c.txt"`. `dirname("/a/b/c.txt")` → `"/a/b"`.
- `file.path` НЕ нормализует пути (не разрешает `..`); для этого используйте `normalizePath()`.
---
## list.files
- `list.files(pattern = "*.csv")` — `pattern` является **регулярным выражением**, а не glob-шаблоном! Используйте `glob2rx("*.csv")` или `"\\.csv$"`.
- `full.names = FALSE` (по умолчанию) возвращает только базовые имена. Для полных путей используйте `full.names = TRUE`.
- `recursive = TRUE` — для поиска в подкаталогах.
- `all.files = TRUE` — для включения скрытых файлов (начинающихся с `.`).
---
## file.info
- Возвращает фрейм данных со столбцами `size`, `isdir`, `mode`, `mtime`, `ctime`, `atime`, `uid`, `gid`.
- `mtime`: время изменения (POSIXct). Полезно использовать `file.info(f)$mtime`.
- В некоторых файловых системах `ctime` — время изменения статуса, а не время создания.
---
## file_test
- `file_test("-f", path)`: TRUE, если обычный файл существует.
- `file_test("-d", path)`: TRUE, если каталог существует.
- `file_test("-nt", f1, f2)`: TRUE, если f1 новее f2.
- Надёжнее, чем `file.exists()`, для различения файлов и каталогов.
references/io-and-text.md
# Ввод-вывод и обработка текста — краткий справочник
> Неочевидное поведение, подводные камни и непростые значения по умолчанию функций R.
> Только то, чего Claude ещё не знает.
---
## read.table (подводные камни)
- `sep = ""` (по умолчанию) означает **любой пробельный символ** (пробелы, табуляции, переводы строк), а не буквальную пустую строку.
- `comment.char = "#"` по умолчанию — строки с `#` усекаются. Для отключения используйте `comment.char = ""` (это также быстрее).
- Автоопределение `header`: устанавливается TRUE, если в первой строке **на одно поле меньше**, чем в последующих (предполагается, что недостающее поле — имена строк).
- `colClasses = "NULL"` полностью **пропускает** этот столбец — очень полезно для скорости.
- Значения по умолчанию `read.csv` отличаются от `read.table`: `header = TRUE`, `sep = ","`, `fill = TRUE`, `comment.char = ""`.
- Для больших файлов: указание `colClasses` и `nrows` резко уменьшает использование памяти. `read.table` медленна для широких фреймов данных (сотни столбцов); используйте `scan` или `data.table::fread` для матриц.
- `stringsAsFactors = FALSE` начиная с R 4.0.0 (раньше было TRUE).
---
## write.table (подводные камни)
- `row.names = TRUE` по умолчанию — создаёт безымянный первый столбец, мешающий повторному чтению. Используйте `row.names = FALSE` или `col.names = NA` для CSV, совместимого с Excel.
- `write.csv` фиксирует `sep = ","`, `dec = "."` и использует `qmethod = "double"` — переопределить их через `...` нельзя.
- `quote = TRUE` (по умолчанию) заключает символьные/факторные столбцы в кавычки. Числовые столбцы никогда не заключаются в кавычки.
- Матрицеподобные столбцы во фреймах данных незаметно разворачиваются в несколько столбцов.
- Медленна для фреймов данных с большим числом столбцов (сотни и больше); каждый столбец обрабатывается отдельно в соответствии с классом.
---
## read.fwf
- Читает файлы с полями фиксированной ширины. `widths` — вектор ширин полей.
- **Отрицательные значения ширины пропускают** соответствующее число символов (полезно для игнорирования полей).
- `buffersize` управляет числом строк, читаемых за один раз; увеличивайте для больших файлов.
- Внутри использует `read.table` после разделения полей.
---
## count.fields
- Подсчитывает поля в каждой строке файла — полезно для диагностики ошибок чтения.
- Аргументы `sep` и `quote` соответствуют аргументам `read.table`.
---
## grep / grepl / sub / gsub (подводные камни)
- Три режима регулярных выражений: расширенный POSIX (по умолчанию), `perl = TRUE`, `fixed = TRUE`. В крайних случаях ведут себя по-разному.
- **Явно именуйте аргументы** — безымянные аргументы после `x`/`pattern` позиционно сопоставляются с `ignore.case`, `perl` и т. д. Частый источник незаметных ошибок.
- `sub` заменяет только **первое** совпадение; `gsub` заменяет **все** совпадения.
- Обратные ссылки: `"\\1"` в замене (двойной обратный слеш в строках R). С `perl = TRUE`: `"\\U\\1"` для преобразования в верхний регистр.
- `grep(value = TRUE)` возвращает совпавшие **элементы**; `grep(value = FALSE)` (по умолчанию) возвращает **индексы**.
- `grepl` возвращает логический вектор — предпочтительна для фильтрации.
- `regexpr` возвращает позицию первого совпадения + длину (как атрибуты); `gregexpr` возвращает все совпадения списком.
- `regexec` возвращает позиции совпадения + захватывающих групп; `gregexec` делает это для всех совпадений.
- Классы символов наподобие `[:alpha:]` в режиме POSIX должны находиться внутри `[[:alpha:]]` (двойные скобки).
---
## strsplit
- Возвращает **список** (один элемент на каждую входную строку), даже для единственной строки.
- `split = ""` или `split = character(0)` разбивает на отдельные символы.
- Совпадение в начале строки: первый элемент результата — `""`. Совпадение в конце: завершающего `""` нет.
- `fixed = TRUE` работает быстрее и исключает интерпретацию как регулярного выражения.
- Частая ошибка: безымянные аргументы незаметно сопоставляются с `fixed`, `perl` и т. д.
---
## substr / substring
- `substr(x, start, stop)`: извлекает/заменяет подстроку. Индексация с 1, обе границы включаются.
- `substring(x, first, last)`: то же, но `last` по умолчанию равно `1000000L` (фактически «до конца»). Векторизована по `first`/`last`.
- Форма присваивания: `substr(x, 1, 3) <- "abc"` выполняет замену непосредственно в объекте (замена должна иметь ту же длину).
---
## trimws
- `which = "both"` (по умолчанию), `"left"` или `"right"`.
- `whitespace = "[ \\t\\r\\n]"` — настраиваемое регулярное выражение, определяющее, что считать пробельным символом.
---
## nchar
- `type = "bytes"` считает байты; `type = "chars"` (по умолчанию) считает символы; `type = "width"` считает ширину отображения.
- `nchar(NA)` возвращает `NA` (не 2). `nchar(factor)` работает с метками уровней.
- `keepNA = TRUE` (по умолчанию с R 3.3.0); задайте `FALSE`, чтобы считать `"NA"` двумя символами.
---
## format / formatC
- `format(x, digits, nsmall)`: `nsmall` задаёт минимальное число знаков после десятичного разделителя. `big.mark = ","` добавляет разделитель тысяч.
- `formatC(x, format = "f", digits = 2)`: форматирование в стиле C. `format = "e"` — научная запись, `"g"` — общий формат.
- `format` возвращает символьный вектор; по умолчанию всегда выравнивает вправо (`justify = "right"`).
---
## type.convert
- Преобразует символьные векторы в подходящие типы (логический, целочисленный, double, комплексный, символьный).
- `as.is = TRUE` (рекомендуется): сохраняет символьные данные символьными, а не факторами.
- Применяется по столбцам фреймов данных. `tryLogical = TRUE` (R 4.3+) преобразует столбцы "TRUE"/"FALSE".
---
## Rscript
- `commandArgs(trailingOnly = TRUE)` получает аргументы скрипта (без флагов R/Rscript).
- Строка `#!` в Unix: `/usr/bin/env Rscript` или полный путь.
- `--vanilla` или `--no-init-file` — для пропуска загрузки `.Rprofile`.
- Код выхода: `quit(status = 1)` для завершения с ошибкой.
---
## capture.output
- Перехватывает вывод `cat`, `print` или любого выражения, записывающего в stdout.
- `file = NULL` (по умолчанию) возвращает символьный вектор. `file = "out.txt"` записывает непосредственно в файл.
- `type = "message"` вместо этого перехватывает stderr.
---
## URLencode / URLdecode
- `URLencode(url, reserved = FALSE)` по умолчанию НЕ кодирует зарезервированные символы (`/`, `?`, `&` и т. д.).
- Задайте `reserved = TRUE` для кодирования **компонента** URL (значения параметра запроса).
---
## glob2rx
- Преобразует glob-шаблоны оболочки в регулярные выражения: `glob2rx("*.csv")` → `"^.*\\.csv$"`.
- Полезно вместе с `list.files(pattern = glob2rx("data_*.RDS"))`.
references/modeling.md
# Моделирование — краткий справочник
> Неочевидное поведение, подводные камни и непростые значения по умолчанию функций R.
> Только то, чего Claude ещё не знает.
---
## formula
Подводные камни символьного задания модели.
- `I()` необходима для буквального использования арифметических операторов: `y ~ x + I(x^2)`. Без `I()` оператор `^` означает комбинации взаимодействий.
- `*` = главные эффекты + взаимодействие: `a*b` разворачивается в `a + b + a:b`.
- `(a+b+c)^2` = все главные эффекты + все двухфакторные взаимодействия (не возведение в квадрат).
- `-` удаляет члены: `(a+b+c)^2 - a:b` исключает только взаимодействие `a:b`.
- `/` означает вложенность: `a/b` = `a + b %in% a` = `a + a:b`.
- `.` в формуле означает «все остальные столбцы данных» (в контексте `terms.formula`) или «предыдущее содержимое» (в `update.formula`).
- Объекты формул содержат **окружение**, используемое для поиска переменных; `as.formula("y ~ x")` использует `parent.frame()`.
---
## terms / model.matrix
- `model.matrix` создаёт матрицу плана, включая кодирование фиктивными переменными. Контрасты по умолчанию: `contr.treatment` для неупорядоченных факторов, `contr.poly` для упорядоченных.
- Атрибуты объекта `terms`: `order` (порядок взаимодействия для каждого члена), `intercept`, матрица `factors`.
- Имена столбцов из `model.matrix` могут быть неожиданными: например, конкатенация `factorLevelName`.
---
## glm
- По умолчанию `family = gaussian(link = "identity")` — `glm()` без `family` незаметно строит OLS-модель (то же, что `lm`, но медленнее и с выводом на основе девианса).
- Распространённые семейства: `binomial(link = "logit")`, `poisson(link = "log")`, `Gamma(link = "inverse")`, `inverse.gaussian()`.
- `binomial` принимает отклик в виде: вектора 0/1, логических значений, фактора (второй уровень = успех) или матрицы с 2 столбцами `cbind(success, failure)`.
- `weights` в `glm` означает **априорные веса** (не частотные веса) — для частотных весов используйте приём с cbind или offset.
- `predict.glm(type = "response")` — для предсказанных вероятностей; по умолчанию `type = "link"` возвращает логарифм шансов (для логистической модели) или логарифм интенсивности (для модели Пуассона).
- `anova(glm_obj, test = "Chisq")` — для тестов на основе девианса; `"F"` недопустим для негауссовых семейств.
- Квазисемейства (`quasibinomial`, `quasipoisson`) допускают сверхдисперсию — AIC не вычисляется.
- Сходимость: `control = glm.control(maxit = 100)`, если стандартных 25 итераций недостаточно.
---
## aov
- `aov` — обёртка над `lm`, сохраняющая дополнительную информацию для сбалансированного ANOVA. Для несбалансированных планов вычисляются SS типа I (последовательные) — порядок членов важен.
- Для SS типа III используйте `car::Anova()` или установите контрасты `contr.sum`/`contr.helmert`.
- Страты ошибки для повторных измерений: `aov(y ~ A*B + Error(Subject/B))`.
- `summary.aov` выдаёт таблицу ANOVA; `summary.lm(aov_obj)` — сводку в стиле регрессии.
---
## nls
- Требует **хороших начальных значений** в `start = list(...)`, иначе сходимость не достигается.
- Самозапускающиеся модели (`SSlogis`, `SSasymp` и т. д.) автоматически вычисляют начальные значения.
- Алгоритм `"port"` допускает ограничения параметров (`lower`/`upper`).
- Если данные описываются слишком точно (нет остаточного шума), проверка сходимости не проходит — используйте `control = list(scaleOffset = 1)` или добавьте небольшой случайный шум к данным.
- Аргумент `weights` — для взвешенного NLS; `na.action` — для обработки пропущенных значений.
---
## step / add1
- `step` выполняет **пошаговый** выбор модели по AIC (по умолчанию). Используйте `k = log(n)` для BIC.
- Направление: `direction = "both"` (по умолчанию), `"forward"` или `"backward"`.
- `add1`/`drop1` оценивают добавления/удаления одного члена; `step` вызывает их итеративно.
- Аргумент `scope` задаёт верхнюю/нижнюю границы модели для поиска.
- `step` изменяет объект модели непосредственно — может работать медленно для больших моделей с множеством потенциальных членов.
---
## predict.lm / predict.glm
- `predict.lm` с `interval = "confidence"` даёт доверительный интервал для **среднего** отклика; `interval = "prediction"` даёт предиктивный интервал для **нового наблюдения** (более широкий).
- `newdata` должен содержать столбцы, соответствующие переменным исходной формулы; факторы должны иметь те же уровни.
- `predict.glm` с `type = "response"` выдаёт прогнозы в шкале отклика (например, вероятности для логистической модели); `type = "link"` (по умолчанию) — в шкале функции связи.
- `se.fit = TRUE` возвращает стандартные ошибки; для `predict.glm` они задаются в шкале **функции связи** независимо от `type`.
- `predict.lm` с `type = "terms"` возвращает вклад каждого члена.
---
## loess
- `span` управляет гладкостью (по умолчанию 0.75). Span < 1 использует такую долю точек; span > 1 использует все точки с скорректированным расстоянием.
- Максимум **4 предиктора**. Использование памяти приблизительно **квадратично** по n (1000 точек ~ 10MB).
- `degree = 0` (локальная константа) допускается, но плохо протестировано — используйте осторожно.
- Не идентична `loess` из S; обусловливание не реализовано.
- `normalize = TRUE` (по умолчанию) стандартизирует предикторы к общей шкале; для пространственных координат задавайте `FALSE`.
---
## lowess в сравнении с loess
- `lowess` — более старая функция; возвращает `list(x, y)` — не позволяет прогнозировать в новых точках.
- `loess` — более новый интерфейс формул с методом `predict`.
- Параметр `lowess` — `f` (span, по умолчанию 2/3); параметр `loess` — `span` (по умолчанию 0.75).
- Для `lowess` значение `iter` по умолчанию равно 3 (итерации повышения робастности); для `loess` по умолчанию `family = "gaussian"` (без робастности).
---
## smooth.spline
- По умолчанию параметр сглаживания выбирается через **GCV** (обобщённую перекрёстную проверку).
- `cv = TRUE` вместо этого использует обычную перекрёстную проверку с исключением одного наблюдения — не используйте при повторяющихся значениях x.
- `spar` и `lambda` управляют гладкостью; `df` может задавать эквивалентное число степеней свободы.
- Возвращает объект с методами `predict`, `print`, `plot`. Компонент `fit` содержит узлы и коэффициенты.
---
## optim
- По умолчанию **минимизирует**. Для максимизации задайте `control = list(fnscale = -1)`.
- Метод по умолчанию — Нелдера — Мида (без градиентов, устойчивый, но медленный). Плохо подходит для одномерных задач — используйте `"Brent"` или `optimize()`.
- `"L-BFGS-B"` — единственный метод, поддерживающий прямоугольные ограничения (`lower`/`upper`). Задание границ автоматически выбирает этот метод с предупреждением.
- `"SANN"` (имитация отжига): код сходимости **всегда 0** — он никогда не «завершается неудачей». `maxit` = общее число вычислений функции (по умолчанию 10000), другого критерия остановки нет.
- `parscale`: масштабируйте параметры так, чтобы изменение каждого на единицу давало сопоставимое изменение целевой функции. Критически важно для задач с разными масштабами.
- `hessian = TRUE`: возвращает численный гессиан задачи **без ограничений**, даже если прямоугольные ограничения активны.
- `fn` может возвращать `NA`/`Inf` (кроме `"L-BFGS-B"`, который всегда требует конечных значений). Начальное значение должно быть конечным.
---
## optimize / uniroot
- `optimize`: одномерная минимизация на ограниченном интервале. Возвращает `minimum` и `objective`.
- `uniroot`: находит корень `f` в `[lower, upper]`. **Требует**, чтобы `f(lower)` и `f(upper)` имели противоположные знаки.
- `uniroot` с `extendInt = "yes"` может автоматически расширять интервал для поиска смены знака — но может находить ложные корни у функций, которые на самом деле не пересекают ноль.
- `nlm`: минимизатор ньютоновского типа. Градиент/гессиан задаются как **атрибуты** возвращаемого `fn` значения (необычный интерфейс).
---
## TukeyHSD
- Требует обученный объект `aov` (не `lm`).
- По умолчанию `conf.level = 0.95`. Возвращает скорректированные p-значения и доверительные интервалы для всех попарных сравнений.
- Имеет смысл только для **сбалансированных** или почти сбалансированных планов; для сильно несбалансированных данных может быть излишне либеральным.
---
## anova (для lm)
- `anova(model)`: последовательные SS (тип I) — **порядок членов важен**.
- `anova(model1, model2)`: F-тест для сравнения вложенных моделей.
- Для SS типа II или III используйте `car::Anova()`.
references/statistics.md
# Статистика — краткий справочник > Неочевидное поведение, подводные камни и непростые значения по умолчанию функций R. > Только то, чего Claude ещё не знает. --- ## chisq.test - `correct = TRUE` (по умолчанию) применяет поправку Йейтса на непрерывность **только для таблиц 2x2**. - `simulate.p.value = TRUE`: метод Монте-Карло с `B = 2000` повторениями (минимальное p ~ 0.0005). Моделирование предполагает **фиксированные маргинальные суммы** (выборка в стиле Фишера, а не предположение хи-квадрат). - Для проверки согласия: передавайте вектор, не матрицу. Сумма `p` должна равняться 1 (или задайте `rescale.p = TRUE`). - Возвращаемый объект включает `$expected`, `$residuals` (Пирсона) и `$stdres` (стандартизированные). --- ## wilcox.test - `exact = TRUE` по умолчанию для малых выборок без связанных рангов. При связанных рангах используется нормальная аппроксимация. - `correct = TRUE` применяет поправку на непрерывность к нормальной аппроксимации. - `conf.int = TRUE` вычисляет оценку Ходжеса — Лемана и доверительный интервал (не только p-значение). - Парный тест: `paired = TRUE` использует критерий знаковых рангов (Уилкоксона), а не суммы рангов (Манна — Уитни). --- ## fisher.test - Для таблиц больше 2x2 использует моделирование (`simulate.p.value = TRUE`) или сетевой алгоритм. - `workspace` управляет памятью для сетевого алгоритма; увеличивайте, если возникают ошибки на больших таблицах. - Аргумент `or` проверяет конкретное отношение шансов (по умолчанию 1) — только для таблиц 2x2. --- ## ks.test - Двухвыборочный тест или одновыборочный тест против эталонного распределения. - **Не** справляется хорошо с совпадающими значениями — выдаёт предупреждение и использует асимптотическую аппроксимацию. - Для составных гипотез (параметры оцениваются по данным) p-значения **консервативны** (слишком велики). Используйте `dgof` или `ks.test` с `exact = NULL` для дискретных распределений. --- ## p.adjust - Методы: `"holm"` (по умолчанию), `"BH"` (FDR Бенджамини — Хохберга), `"bonferroni"`, `"BY"`, `"hochberg"`, `"hommel"`, `"fdr"` (псевдоним BH), `"none"`. - Аргумент `n`: общее число гипотез (может быть больше `length(p)`, если некоторые p-значения исключены). - Обрабатывает `NA`: скорректированные p-значения равны `NA` там, где входное значение — `NA`. --- ## pairwise.t.test / pairwise.wilcox.test - `p.adjust.method` по умолчанию равен `"holm"`. Измените на `"BH"` для контроля FDR. - `pool.sd = TRUE` (по умолчанию для t-теста): использует объединённое стандартное отклонение по всем группам (предполагает равные дисперсии). - Возвращает матрицу p-значений, а не статистик теста. --- ## shapiro.test - Размер выборки должен быть от 3 до 5000. - Проверяет нормальность; низкое p-значение = свидетельство против нормальности. --- ## kmeans - Рекомендуется `nstart > 1` (например, `nstart = 25`): запускает алгоритм из нескольких случайных начальных состояний и возвращает лучшее. - По умолчанию `iter.max = 10` — может быть слишком мало для сходимости. Увеличивайте для больших/сложных данных. - Алгоритм по умолчанию — "Hartigan-Wong" (обычно лучший). Очень близкие точки могут вызвать несходимость (предупреждение с `ifault = 4`). - Нумерация кластеров произвольна; порядок может различаться между платформами. - Всегда возвращает k кластеров, когда задано k (кроме Lloyd-Forgy, который может вернуть меньше). --- ## hclust - `method = "ward.D2"` корректно реализует критерий Уорда (с использованием квадратов расстояний). Более старый `"ward.D"` не возводил расстояния в квадрат (сохранён для обратной совместимости). - Вход должен быть объектом `dist`. Используйте `as.dist()` для преобразования симметричной матрицы. - `hang = -1` в `plot()` выравнивает все подписи внизу. --- ## dist - `method = "euclidean"` (по умолчанию). Другие варианты: `"manhattan"`, `"maximum"`, `"canberra"`, `"binary"`, `"minkowski"`. - Возвращает объект `dist` (только нижний треугольник). Для полной матрицы используйте `as.matrix()`. - `"canberra"`: члены с нулевыми числителем и знаменателем **исключаются** из суммы (не трактуются как 0/0). - Значения `Inf`: евклидово расстояние с участием `Inf` равно `Inf`. Несколько `Inf` в одном наблюдении дают `NaN` для некоторых методов. --- ## prcomp в сравнении с princomp - `prcomp` использует **SVD** (численно более совершенный подход); `princomp` использует `eigen` для ковариации (менее устойчивый, масштабирование N-1 в сравнении с N). - `scale. = TRUE` в `prcomp` стандартизирует переменные; важно, если масштабы переменных сильно различаются. - Стандартные отклонения `princomp` отличаются от `prcomp` множителем `sqrt((n-1)/n)`. - Обе возвращают `$rotation` (нагрузки) и `$x` (значения компонент); знаки компонент могут различаться между запусками. --- ## density - Ширина окна по умолчанию: `bw = "nrd0"` (эмпирическое правило Сильвермана). Для мультимодальных данных рассмотрите `"SJ"` или `"bcv"`. - `adjust`: множитель ширины окна. `adjust = 0.5` уменьшает ширину вдвое (меньше сглаживания). - Ядро по умолчанию: `"gaussian"`. Диапазон плотности выходит за диапазон данных (управляется `cut`, по умолчанию 3 ширины окна). - `n = 512`: число точек вычисления. Увеличивайте для более гладкого графика. - `from`/`to`: явно ограничивают диапазон вычисления. --- ## quantile - **Девять** вариантов `type` (1–9). По умолчанию `type = 7` (стандарт R, линейная интерполяция). Тип 1 = обратная функция эмпирической CDF (по умолчанию в SAS). Типы 4–9 непрерывны; 1–3 разрывны. - `na.rm = FALSE` по умолчанию — возвращает NA при наличии любых NA. - `names = TRUE` по умолчанию, добавляет "0%", "25%" и т. д. в качестве имён. --- ## Распределения (общие подводные камни) Все функции распределений следуют шаблону `d/p/q/r`. Общие неочевидные моменты: - **Аргумент `n` в функциях `r*()`**: если `length(n) > 1`, в качестве количества используется `length(n)`, а не само `n`. Поэтому `rnorm(c(1,2,3))` генерирует 3 значения, а не 1+2+3. - `log = TRUE` / `log.p = TRUE`: вычисления в логарифмической шкале для численной устойчивости в хвостах. - `lower.tail = FALSE` непосредственно даёт функцию выживания P(X > x) (в хвостах точнее, чем 1 - pnorm()). - **Гамма-распределение**: параметризуется через `shape` и `rate` (= 1/scale). По умолчанию `rate = 1`. Одновременное указание `rate` и `scale` — ошибка. - **Бета-распределение**: `shape1` (альфа), `shape2` (бета) — параметризации через `mean`/`sd` нет. - **Пуассон `dpois`**: `x` может быть нецелым (для нецелых значений возвращает 0 с предупреждением, если `log = FALSE`). - **Вейбулл**: `shape` и `scale` (без `rate`). Параметризация R: `f(x) = (shape/scale)(x/scale)^(shape-1) exp(-(x/scale)^shape)`. - **Логнормальное распределение**: `meanlog` и `sdlog` — среднее/стандартное отклонение **логарифма**, а не самого распределения. --- ## cor.test - Метод по умолчанию: `"pearson"`. Также `"kendall"` и `"spearman"`. - Возвращает `$estimate`, `$p.value`, `$conf.int` (доверительный интервал только для Пирсона). - Интерфейс формул: `cor.test(~ x + y, data = df)` — обратите внимание на `~` без левой части. --- ## ecdf - Возвращает **функцию** (ступенчатую). Вызывайте её для новых значений: `Fn <- ecdf(x); Fn(3.5)`. - `plot(ecdf(x))` строит график эмпирической CDF. - Возвращаемая функция непрерывна справа и имеет левые пределы (cadlag). --- ## weighted.mean - Обрабатывает `NA` в весах: наблюдение исключается, если вес равен `NA`. - Сумма весов не обязана равняться 1; они нормализуются внутри функции.
references/visualization.md
# Визуализация — краткий справочник
> Неочевидное поведение, подводные камни и непростые значения по умолчанию функций R.
> Только то, чего Claude ещё не знает.
---
## par (подводные камни)
- Настройки `par()` относятся к конкретному устройству. Открытие нового устройства всё сбрасывает.
- Установка `mfrow`/`mfcol` сбрасывает `cex` в 1 и `mex` в 1. При размещении 2x2 базовое `cex` умножается на 0.83; при 3 и более строках/столбцах — на 0.66.
- `mai` (дюймы), `mar` (строки), `pin`, `plt`, `pty` взаимодействуют друг с другом. Восстановление всех сохранённых параметров после изменения размера устройства может дать несогласованные результаты — побеждает последний по алфавиту.
- Установка `bg` через `par()` также задаёт `new = FALSE`. Установка `fg` через `par()` также задаёт `col`.
- `xpd = NA` обрезает по области устройства (позволяет рисовать на внешних полях); `xpd = TRUE` обрезает по области рисунка; `xpd = FALSE` (по умолчанию) обрезает по области графика.
- `mgp = c(3, 1, 0)`: управляет строкой заголовка (`mgp[1]`), строкой подписей (`mgp[2]`), линией оси (`mgp[3]`). Всё в единицах `mex`.
- `las`: 0 = параллельно оси, 1 = горизонтально, 2 = перпендикулярно, 3 = вертикально. **Не** реагирует на `srt`.
- `tck = 1` рисует линии сетки через весь график. `tcl = -0.5` (по умолчанию) даёт засечки наружу.
- `usr` при логарифмической шкале: содержит **log10** границ координат, а не исходные значения.
- Параметры только для чтения: `cin`, `cra`, `csi`, `cxy`, `din`, `page`.
---
## layout
- `layout(mat)`, где `mat` — матрица целых чисел, задающая расположение рисунков.
- `widths`/`heights` принимают `lcm()` для абсолютных размеров, смешанных с относительными.
- Гибче, чем `mfrow`/`mfcol`, но после установки прочитать конфигурацию нельзя (в отличие от `par("mfrow")`).
- `layout.show(n)` визуализирует расположение для отладки.
---
## axis / mtext
- `axis(side, at, labels)`: `side` 1=снизу, 2=слева, 3=сверху, 4=справа.
- Интервал между подписями осей по умолчанию управляется `par("mgp")`. Без контроля подписи могут перекрываться.
- `mtext`: аргумент `line` размещает текст по строкам поля (0 = рядом с графиком, положительное значение = наружу). `adj` управляет горизонтальным положением (0–1).
- `mtext` с `outer = TRUE` пишет на **внешнем** поле (задаётся через `par(oma = ...)`).
---
## curve
- Первый аргумент может быть **выражением** от `x` или функцией: `curve(sin, 0, 2*pi)` или `curve(x^2 + 1, 0, 10)`.
- `add = TRUE` — для наложения на существующий график. По умолчанию `n = 101` точка вычисления.
- По умолчанию `xname = "x"`; измените, если в выражении используется другое имя переменной.
---
## pairs
- Функция `panel` получает `(x, y, ...)` для каждой пары. `lower.panel`, `upper.panel`, `diag.panel` — для разных областей.
- `gap` управляет промежутками между панелями (по умолчанию 1).
- Интерфейс формул: `pairs(~ var1 + var2 + var3, data = df)`.
---
## coplot
- Условные графики: `coplot(y ~ x | a)` или `coplot(y ~ x | a * b)` для двух обусловливающих переменных.
- Функцию `panel` можно настраивать; `rows`/`columns` управляют расположением.
- Панель по умолчанию рисует точки; используйте `panel = panel.smooth` для наложения loess.
---
## matplot / matlines / matpoints
- Строит графики столбцов одной матрицы относительно столбцов другой. Циклически повторяет `col`, `lty`, `pch` для столбцов.
- По умолчанию `type = "l"` (в отличие от `plot`, где по умолчанию `"p"`).
- Полезно для одновременного отображения нескольких временных рядов или подобранных кривых.
---
## contour / filled.contour / image
- `contour(x, y, z)`: `z` должна быть матрицей с `dim = c(length(x), length(y))`.
- `filled.contour` имеет нестандартное расположение — создаёт собственную область графика для цветовой шкалы. **Нельзя использовать с ней `par(mfrow)`**. Для добавления элементов требуется аргумент `plot.axes`.
- `image`: отображает значения z как цветные прямоугольники. Цветовая схема по умолчанию может вводить в заблуждение; задавайте `col` явно.
- Для `image` переменные `x` и `y` задают **границы ячеек** или **середины** в зависимости от контекста.
---
## persp
- `persp(x, y, z, theta, phi)`: `theta` = азимутальный угол, `phi` = полярный угол.
- Возвращает **матрицу преобразования** (невидимо) для проекции 3D в 2D — используйте `trans3d()` для добавления точек/линий на перспективный график.
- `shade` и `col` управляют затенением поверхности. `border = NA` убирает линии сетки.
---
## segments / arrows / rect / polygon
- Все принимают векторизованные координаты; при необходимости циклически повторяют значения.
- `arrows`: `code = 1` (наконечник в начале), `code = 2` (наконечник в конце, по умолчанию), `code = 3` (с обоих концов).
- `polygon`: последняя точка автоматически соединяется с первой. Заливка через `col`; `border` управляет контуром.
- `rect(xleft, ybottom, xright, ytop)` — обратите внимание: порядок аргументов отличается от других систем.
---
## dev / dev.off / dev.copy
- `dev.new()` открывает новое устройство. `dev.off()` закрывает текущее устройство (и сбрасывает вывод для файловых устройств, таких как `pdf`).
- `dev.off()` для **последнего** открытого устройства возвращает к нулевому устройству.
- `dev.copy(pdf, file = "plot.pdf")`, затем `dev.off()` — для сохранения текущего графика.
- `dev.list()` возвращает все открытые устройства; `dev.cur()` — активное.
---
## pdf
- Для завершения записи файла необходимо вызвать `dev.off()`. Без этого файл может быть пустым/повреждённым.
- `onefile = TRUE` (по умолчанию): несколько страниц в одном PDF. `onefile = FALSE`: по одному файлу на страницу (использует `%d` в имени файла для нумерации).
- Рекомендуется `useDingbats = FALSE`, чтобы избежать проблем с некоторыми средствами просмотра PDF и символами pch.
- Размер по умолчанию: 7x7 дюймов. `family` управляет семейством шрифта.
---
## png / растровые устройства
- `res` управляет DPI (по умолчанию 72). Для публикации: `res = 300` с подходящими `width`/`height` в пикселях или дюймах (с `units = "in"`).
- `type = "cairo"` (в системах с cairo) даёт лучшее сглаживание, чем стандартный вариант.
- `bg = "transparent"` — для прозрачного фона (PNG поддерживает альфа-канал).
---
## colors / rgb / hcl / col2rgb
- `colors()` возвращает все 657 именованных цветов. `col2rgb("color")` возвращает матрицу RGB.
- `rgb(r, g, b, alpha, maxColorValue = 255)` — обратите внимание: по умолчанию `maxColorValue` равен 1, а не 255.
- `hcl(h, c, l)`: перцептивно равномерное цветовое пространство. Предпочтительно для цветовых шкал.
- `adjustcolor(col, alpha.f = 0.5)`: простой способ добавить прозрачность.
---
## colorRamp / colorRampPalette
- `colorRamp` возвращает **функцию**, отображающую [0,1] в матрицу RGB.
- `colorRampPalette` возвращает **функцию**, принимающую `n` и возвращающую `n` интерполированных цветов.
- `space = "Lab"` даёт более равномерную по восприятию интерполяцию, чем `"rgb"`.
---
## palette / recordPlot
- `palette()` возвращает текущую палитру (по умолчанию 8 цветов). `palette("Set1")` устанавливает встроенную палитру.
- Целочисленные значения цвета на графиках индексируют палитру (с циклическим повторением). Индекс 0 = цвет фона.
- `recordPlot()` / `replayPlot()`: сохраняют и восстанавливают полный график — зависят от устройства и ненадёжны между сессиями.
assets/analysis_template.R
# ============================================================
# Analysis Template — Base R
# Copy this file, rename it, and fill in your details.
# ============================================================
# Author :
# Date :
# Data :
# Purpose :
# ============================================================
# ── 0. Setup ─────────────────────────────────────────────────
# Clear environment (optional — comment out if loading into existing session)
rm(list = ls())
# Set working directory if needed
# setwd("/path/to/your/project")
# Reproducibility
set.seed(42)
# Libraries — uncomment what you need
# library(haven) # read .dta / .sav / .sas
# library(readxl) # read Excel files
# library(openxlsx) # write Excel files
# library(foreign) # older Stata / SPSS formats
# library(survey) # survey-weighted analysis
# library(lmtest) # Breusch-Pagan, Durbin-Watson etc.
# library(sandwich) # robust standard errors
# library(car) # Type II/III ANOVA, VIF
# ── 1. Load Data ─────────────────────────────────────────────
df <- read.csv("your_data.csv", stringsAsFactors = FALSE)
# df <- readRDS("your_data.rds")
# df <- haven::read_dta("your_data.dta")
# First look — always run these
dim(df)
str(df)
head(df, 10)
summary(df)
# ── 2. Data Quality Check ────────────────────────────────────
# Missing values
na_report <- data.frame(
column = names(df),
n_miss = colSums(is.na(df)),
pct_miss = round(colMeans(is.na(df)) * 100, 1),
row.names = NULL
)
print(na_report[na_report$n_miss > 0, ])
# Duplicates
n_dup <- sum(duplicated(df))
cat(sprintf("Duplicate rows: %d\n", n_dup))
# Unique values for categorical columns
cat_cols <- names(df)[sapply(df, function(x) is.character(x) | is.factor(x))]
for (col in cat_cols) {
cat(sprintf("\n%s (%d unique):\n", col, length(unique(df[[col]]))))
print(table(df[[col]], useNA = "ifany"))
}
# ── 3. Clean & Transform ─────────────────────────────────────
# Rename columns (example)
# names(df)[names(df) == "old_name"] <- "new_name"
# Convert types
# df$group <- as.factor(df$group)
# df$date <- as.Date(df$date, format = "%Y-%m-%d")
# Recode values (example)
# df$gender <- ifelse(df$gender == 1, "Male", "Female")
# Create new variables (example)
# df$log_income <- log(df$income + 1)
# df$age_group <- cut(df$age,
# breaks = c(0, 25, 45, 65, Inf),
# labels = c("18-25", "26-45", "46-65", "65+"))
# Filter rows (example)
# df <- df[df$year >= 2010, ]
# df <- df[complete.cases(df[, c("outcome", "predictor")]), ]
# Drop unused factor levels
# df <- droplevels(df)
# ── 4. Descriptive Statistics ────────────────────────────────
# Numeric summary
num_cols <- names(df)[sapply(df, is.numeric)]
round(sapply(df[num_cols], function(x) c(
n = sum(!is.na(x)),
mean = mean(x, na.rm = TRUE),
sd = sd(x, na.rm = TRUE),
median = median(x, na.rm = TRUE),
min = min(x, na.rm = TRUE),
max = max(x, na.rm = TRUE)
)), 3)
# Cross-tabulation
# table(df$group, df$category, useNA = "ifany")
# prop.table(table(df$group, df$category), margin = 1) # row proportions
# ── 5. Visualization (EDA) ───────────────────────────────────
par(mfrow = c(2, 2))
# Histogram of main outcome
hist(df$outcome_var,
main = "Distribution of Outcome",
xlab = "Outcome",
col = "steelblue",
border = "white",
breaks = 30)
# Boxplot by group
boxplot(outcome_var ~ group_var,
data = df,
main = "Outcome by Group",
col = "lightyellow",
las = 2)
# Scatter plot
plot(df$predictor, df$outcome_var,
main = "Predictor vs Outcome",
xlab = "Predictor",
ylab = "Outcome",
pch = 19,
col = adjustcolor("steelblue", alpha.f = 0.5),
cex = 0.8)
abline(lm(outcome_var ~ predictor, data = df),
col = "red", lwd = 2)
# Correlation matrix (numeric columns only)
cor_mat <- cor(df[num_cols], use = "complete.obs")
image(cor_mat,
main = "Correlation Matrix",
col = hcl.colors(20, "RdBu", rev = TRUE))
par(mfrow = c(1, 1))
# ── 6. Analysis ───────────────────────────────────────────────
# ·· 6a. Comparison of means ··
t.test(outcome_var ~ group_var, data = df)
# ·· 6b. Linear regression ··
fit <- lm(outcome_var ~ predictor1 + predictor2 + group_var,
data = df)
summary(fit)
confint(fit)
# Check VIF for multicollinearity (requires car)
# car::vif(fit)
# Robust standard errors (requires lmtest + sandwich)
# lmtest::coeftest(fit, vcov = sandwich::vcovHC(fit, type = "HC3"))
# ·· 6c. ANOVA ··
# fit_aov <- aov(outcome_var ~ group_var, data = df)
# summary(fit_aov)
# TukeyHSD(fit_aov)
# ·· 6d. Logistic regression (binary outcome) ··
# fit_logit <- glm(binary_outcome ~ x1 + x2,
# data = df,
# family = binomial(link = "logit"))
# summary(fit_logit)
# exp(coef(fit_logit)) # odds ratios
# exp(confint(fit_logit)) # OR confidence intervals
# ── 7. Model Diagnostics ─────────────────────────────────────
par(mfrow = c(2, 2))
plot(fit)
par(mfrow = c(1, 1))
# Residual normality
shapiro.test(residuals(fit))
# Homoscedasticity (requires lmtest)
# lmtest::bptest(fit)
# ── 8. Save Output ────────────────────────────────────────────
# Cleaned data
# write.csv(df, "data_clean.csv", row.names = FALSE)
# saveRDS(df, "data_clean.rds")
# Model results to text file
# sink("results.txt")
# cat("=== Linear Model ===\n")
# print(summary(fit))
# cat("\n=== Confidence Intervals ===\n")
# print(confint(fit))
# sink()
# Plots to file
# png("figure1_distributions.png", width = 1200, height = 900, res = 150)
# par(mfrow = c(2, 2))
# # ... your plots ...
# par(mfrow = c(1, 1))
# dev.off()
# ============================================================
# END OF TEMPLATE
# ============================================================
scripts/check_data.R
# check_data.R — Quick data quality report for any R data frame
# Usage: source("check_data.R") then call check_data(df)
# Or: source("check_data.R"); check_data(read.csv("yourfile.csv"))
check_data <- function(df, top_n_levels = 8) {
if (!is.data.frame(df)) stop("Input must be a data frame.")
n_row <- nrow(df)
n_col <- ncol(df)
cat("══════════════════════════════════════════\n")
cat(" DATA QUALITY REPORT\n")
cat("══════════════════════════════════════════\n")
cat(sprintf(" Rows: %d Columns: %d\n", n_row, n_col))
cat("══════════════════════════════════════════\n\n")
# ── 1. Column overview ──────────────────────
cat("── COLUMN OVERVIEW ────────────────────────\n")
for (col in names(df)) {
x <- df[[col]]
cls <- class(x)[1]
n_na <- sum(is.na(x))
pct <- round(n_na / n_row * 100, 1)
n_uniq <- length(unique(x[!is.na(x)]))
na_flag <- if (n_na == 0) "" else sprintf(" *** %d NAs (%.1f%%)", n_na, pct)
cat(sprintf(" %-20s %-12s %d unique%s\n",
col, cls, n_uniq, na_flag))
}
# ── 2. NA summary ────────────────────────────
cat("\n── NA SUMMARY ─────────────────────────────\n")
na_counts <- sapply(df, function(x) sum(is.na(x)))
cols_with_na <- na_counts[na_counts > 0]
if (length(cols_with_na) == 0) {
cat(" No missing values. \n")
} else {
cat(sprintf(" Columns with NAs: %d of %d\n\n", length(cols_with_na), n_col))
for (col in names(cols_with_na)) {
bar_len <- round(cols_with_na[col] / n_row * 20)
bar <- paste0(rep("█", bar_len), collapse = "")
pct_na <- round(cols_with_na[col] / n_row * 100, 1)
cat(sprintf(" %-20s [%-20s] %d (%.1f%%)\n",
col, bar, cols_with_na[col], pct_na))
}
}
# ── 3. Numeric columns ───────────────────────
num_cols <- names(df)[sapply(df, is.numeric)]
if (length(num_cols) > 0) {
cat("\n── NUMERIC COLUMNS ────────────────────────\n")
cat(sprintf(" %-20s %8s %8s %8s %8s %8s\n",
"Column", "Min", "Mean", "Median", "Max", "SD"))
cat(sprintf(" %-20s %8s %8s %8s %8s %8s\n",
"──────", "───", "────", "──────", "───", "──"))
for (col in num_cols) {
x <- df[[col]][!is.na(df[[col]])]
if (length(x) == 0) next
cat(sprintf(" %-20s %8.3g %8.3g %8.3g %8.3g %8.3g\n",
col,
min(x), mean(x), median(x), max(x), sd(x)))
}
}
# ── 4. Factor / character columns ───────────
cat_cols <- names(df)[sapply(df, function(x) is.factor(x) | is.character(x))]
if (length(cat_cols) > 0) {
cat("\n── CATEGORICAL COLUMNS ────────────────────\n")
for (col in cat_cols) {
x <- df[[col]]
tbl <- sort(table(x, useNA = "no"), decreasing = TRUE)
n_lv <- length(tbl)
cat(sprintf("\n %s (%d unique values)\n", col, n_lv))
show <- min(top_n_levels, n_lv)
for (i in seq_len(show)) {
lbl <- names(tbl)[i]
cnt <- tbl[i]
pct <- round(cnt / n_row * 100, 1)
cat(sprintf(" %-25s %5d (%.1f%%)\n", lbl, cnt, pct))
}
if (n_lv > top_n_levels) {
cat(sprintf(" ... and %d more levels\n", n_lv - top_n_levels))
}
}
}
# ── 5. Duplicate rows ────────────────────────
cat("\n── DUPLICATES ─────────────────────────────\n")
n_dup <- sum(duplicated(df))
if (n_dup == 0) {
cat(" No duplicate rows.\n")
} else {
cat(sprintf(" %d duplicate row(s) found (%.1f%% of data)\n",
n_dup, n_dup / n_row * 100))
}
cat("\n══════════════════════════════════════════\n")
cat(" END OF REPORT\n")
cat("══════════════════════════════════════════\n")
# Return invisibly for programmatic use
invisible(list(
dims = c(rows = n_row, cols = n_col),
na_counts = na_counts,
n_dupes = n_dup
))
}
scripts/scaffold_analysis.R
#!/usr/bin/env Rscript
# scaffold_analysis.R — Generates a starter analysis script
#
# Usage (from terminal):
# Rscript scaffold_analysis.R myproject
# Rscript scaffold_analysis.R myproject outcome_var group_var
#
# Usage (from R console):
# source("scaffold_analysis.R")
# scaffold_analysis("myproject", outcome = "score", group = "treatment")
#
# Output: myproject_analysis.R (ready to edit)
scaffold_analysis <- function(project_name,
outcome = "outcome",
group = "group",
data_file = NULL) {
if (is.null(data_file)) data_file <- paste0(project_name, ".csv")
out_file <- paste0(project_name, "_analysis.R")
template <- sprintf(
'# ============================================================
# Project : %s
# Created : %s
# ============================================================
# ── 0. Libraries ─────────────────────────────────────────────
# Add packages you need here
# library(ggplot2)
# library(haven) # for .dta files
# library(openxlsx) # for Excel output
# ── 1. Load Data ─────────────────────────────────────────────
df <- read.csv("%s", stringsAsFactors = FALSE)
# Quick check — always do this first
cat("Dimensions:", dim(df), "\\n")
str(df)
head(df)
# ── 2. Explore / EDA ─────────────────────────────────────────
summary(df)
# NA check
na_counts <- colSums(is.na(df))
na_counts[na_counts > 0]
# Key variable distributions
hist(df$%s, main = "Distribution of %s", xlab = "%s")
if ("%s" %%in%% names(df)) {
table(df$%s)
barplot(table(df$%s),
main = "Counts by %s",
col = "steelblue",
las = 2)
}
# ── 3. Clean / Transform ──────────────────────────────────────
# df <- df[complete.cases(df), ] # drop rows with any NA
# df$%s <- as.factor(df$%s) # convert to factor
# ── 4. Analysis ───────────────────────────────────────────────
# Descriptive stats by group
tapply(df$%s, df$%s, mean, na.rm = TRUE)
tapply(df$%s, df$%s, sd, na.rm = TRUE)
# t-test (two groups)
# t.test(%s ~ %s, data = df)
# Linear model
fit <- lm(%s ~ %s, data = df)
summary(fit)
confint(fit)
# ANOVA (multiple groups)
# fit_aov <- aov(%s ~ %s, data = df)
# summary(fit_aov)
# TukeyHSD(fit_aov)
# ── 5. Visualize Results ──────────────────────────────────────
par(mfrow = c(1, 2))
# Boxplot by group
boxplot(%s ~ %s,
data = df,
main = "%s by %s",
xlab = "%s",
ylab = "%s",
col = "lightyellow")
# Model diagnostics
plot(fit, which = 1) # residuals vs fitted
par(mfrow = c(1, 1))
# ── 6. Save Output ────────────────────────────────────────────
# Save cleaned data
# write.csv(df, "%s_clean.csv", row.names = FALSE)
# Save model summary to text
# sink("%s_results.txt")
# summary(fit)
# sink()
# Save plot to file
# png("%s_boxplot.png", width = 800, height = 600, res = 150)
# boxplot(%s ~ %s, data = df, col = "lightyellow")
# dev.off()
',
project_name,
format(Sys.Date(), "%%Y-%%m-%%d"),
data_file,
# Section 2 — EDA
outcome, outcome, outcome,
group, group, group, group,
# Section 3
group, group,
# Section 4
outcome, group,
outcome, group,
outcome, group,
outcome, group,
outcome, group,
outcome, group,
# Section 5
outcome, group,
outcome, group,
group, outcome,
# Section 6
project_name, project_name, project_name,
outcome, group
)
writeLines(template, out_file)
cat(sprintf("Created: %s\n", out_file))
invisible(out_file)
}
# ── Run from command line ─────────────────────────────────────
if (!interactive()) {
args <- commandArgs(trailingOnly = TRUE)
if (length(args) == 0) {
cat("Usage: Rscript scaffold_analysis.R <project_name> [outcome_var] [group_var]\n")
cat("Example: Rscript scaffold_analysis.R myproject score treatment\n")
quit(status = 1)
}
project <- args[1]
outcome <- if (length(args) >= 2) args[2] else "outcome"
group <- if (length(args) >= 3) args[3] else "group"
scaffold_analysis(project, outcome = outcome, group = group)
}
README.md
# base-r-skill
GitHub: https://github.com/iremaydas/base-r-skill
Навык Claude Code для программирования на базовом R.
---
## История
Я аспирантка по политологии, регулярно использую R, но никогда не назвала бы себя *специалистом по R*. Мне был нужен навык Claude Code для базового R — без tidyverse, без ggplot2, просто обычный R, — и я нигде не смогла его найти.
Поэтому я сделала его сама. В 11 вечера. Попросив Claude помочь мне создать навык для Claude.
Если вы тоже каждый раз гуглите `how to drop NA rows in R`, это для вас. 🫶
---
## Что внутри
```
base-r/
├── SKILL.md # Основной файл навыка
├── references/ # Подводные камни и неочевидное поведение
│ ├── data-wrangling.md # Ловушки выбора подмножеств, семейство apply, merge, особенности factor
│ ├── modeling.md # Синтаксис формул, lm/glm/aov/nls, optim
│ ├── statistics.md # Проверка гипотез, распределения, кластеризация
│ ├── visualization.md # par, layout, устройства, цвета
│ ├── io-and-text.md # read.table, grep, регулярные выражения, format
│ ├── dates-and-system.md # Ловушки Date/POSIXct, options(), операции с файлами
│ └── misc-utilities.md # tryCatch, do.call, временные ряды, утилиты
├── scripts/
│ ├── check_data.R # Краткий отчёт о качестве данных для любого фрейма данных
│ └── scaffold_analysis.R # Генерирует стартовый скрипт анализа
└── assets/
└── analysis_template.R # Шаблон анализа для копирования и вставки
```
Справочные файлы были сокращены из официального руководства R 4.5.3 — **19,518 строк → 945 строк** (сокращение на 95%). Осталось только неочевидное: подводные камни, неожиданные значения по умолчанию, сложные взаимодействия. То, что Claude и так хорошо знает, было удалено.
---
## Как использовать
Добавьте этот навык в свою конфигурацию Claude Code, указав данный репозиторий. После этого Claude будет автоматически загружать соответствующие справочные файлы, когда вы работаете над задачами R.
Лучше всего подходит для:
- Преобразования данных в базовом R (без tidyverse)
- Статистического моделирования с `lm`, `glm`, `aov`
- Базовой графики с `plot`, `par`, `barplot`
- Понимания того, почему ваш код R делает что-то странное
Не подходит для: tidyverse, ggplot2, Shiny или разработки пакетов R.
---
## Скрипт `check_data.R`
Вероятно, самая полезная самостоятельная вещь здесь. Подключите его через source и выполните `check_data(df)` для любого фрейма данных, чтобы получить оформленный отчёт о размерностях, количестве NA, числовых сводках и разбивках категориальных данных.
```r
source("scripts/check_data.R")
check_data(your_df)
```
---
## Создано при помощи
- Claude (разумеется)
- Официальных руководств R (всех их 19,518 строк)
- Лёгкого раздражения и нескольких чашек кофе
---
## Участие в проекте
Если заметите пропущенный подводный камень, неправильное значение по умолчанию или что-то, что должно быть в справочниках, — PR очень приветствуются. Я тоже учусь.
---
*Создано [@iremaydas](https://github.com/iremaydas) — аспиранткой, время от времени пользующейся R и постоянно гуглящей вещи, которые мне, наверное, уже пора знать.*Текст доступен бесплатно по CC0 1.0. Источники и лицензии.
Как использовать навык
Прочитайте инструкцию и проверьте, какие файлы, инструменты и подключения ей нужны. Перенесите навык в совместимое приложение для AI-агентов или используйте подходящие шаги в чате. Если навык состоит из нескольких файлов, сохраните их структуру.