Матрица — это вектор с размерностями
Матрица в R — это прямоугольник значений (строки и столбцы), где каждая ячейка хранит один и тот же тип, обычно числа. Под капотом это буквально вектор с приколоченным атрибутом dim, что объясняет большую часть его поведения: один тип насквозь, векторная арифметика повсюду.
Матрицу строят, перестраивая вектор через matrix():
Шесть значений, две строки — R сам вычисляет, что нужно три столбца. dim() сообщает обе размерности сразу как 2 3; nrow() и ncol() дают их по отдельности.
Присмотритесь к напечатанной матрице: значения идут 1 2 вниз по первому столбцу, затем 3 4 вниз по следующему. По умолчанию R заполняет матрицы по столбцам. Если ваши данные читаются по строкам — а именно так их обычно записывают люди, — скажите об этом через byrow = TRUE:
Теперь первая строка — 1 2 3. Забытый byrow = TRUE не вызовет ошибки — он молча даст вам похожее на транспонированное расположение тех же чисел, так что возьмите за привычку проверять напечатанный вид всякий раз, когда строите матрицу из сырых значений.
Индексация: m[строка, столбец]
Индексация матрицы использует две позиции внутри одной пары скобок: строка перед запятой, столбец после — обе считаются с 1:
Пустая позиция означает «все»: m[1, ] — вся первая строка, m[, 2] — весь второй столбец. Обратите внимание, что оба возвращаются как обычные векторы — R отбрасывает размерность, схлопнувшуюся до 1. Это удобно в интерактивной работе и ловушка в коде, потому что функция, ожидающая матрицу, подавится вектором. Попросите R сохранить форму через drop = FALSE:
dim() теперь сообщает 1 3 — это по-прежнему матрица. Всякий раз, когда вы вырезаете одну строку или столбец внутри функции, пишите drop = FALSE; баг, который это предотвращает (код работает на широких данных и ломается на данных из одного столбца), крайне неприятно отлавливать.
Логические маски работают и здесь: m[m > 3] возвращает все ячейки больше 3 в виде вектора.
Сборка через cbind() и rbind()
Вместо перестройки одного длинного вектора матрицу можно собрать из частей: cbind() связывает векторы как столбцы, rbind() — как строки. Те же функции также расширяют существующую матрицу:
Имена путешествуют вместе с векторами: cbind() автоматически использовал heights и weights как имена столбцов, что сохраняет напечатанную матрицу читаемой. Обе функции требуют совпадения длин (с переработкой для значений длины 1); привязка вектора длины 3 к матрице из 4 строк выдаст предупреждение.
Имена столбцов и строк можно также задать напрямую через colnames(m) <- ... и rownames(m) <- ..., после чего вы сможете индексировать по имени: people[, "weights"].
Поэлементное * против настоящего матричного умножения %*%
Вот различие, которое важнее всего во всей этой статье. В R два оператора умножения для матриц, и они вычисляют совершенно разные вещи:
a * a— поэлементное умножение: каждая ячейка на соответствующую ячейку.1 2 3 4становятся1 4 9 16, расположенными в той же форме. Это просто векторная арифметика, тот же*, которым вы пользуетесь на векторах.a %*% a— матричное умножение из линейной алгебры: каждая ячейка результата — это строка первой матрицы, умноженная на столбец второй, с суммированием. Тот же вход даёт7 10 15 22— совершенно другие числа.
Запустите сниппет и сравните два вывода рядом; именно вид разных результатов на одинаковом входе закрепляет различие. Если вы напишете * там, где математика требует %*%, R вас не предупредит — для квадратных матриц формы совместимы в обоих случаях, — вы просто получите неверные числа. В статистическом коде (ковариационные матрицы, алгебра линейных моделей) это один из классических тихих багов.
t() транспонирует — меняет строки и столбцы местами — и постоянно появляется рядом с %*%, потому что матричному умножению нужно совпадение внутренних размерностей:
Для полноты: solve(m) обращает матрицу, а %*% с вектором трактует его как матрицу из одного столбца. Глубже этого работа с данными обычно не заходит.
Сводки по строкам и столбцам
Суммирование и усреднение по строкам и столбцам настолько частая задача, что в R есть специальные быстрые функции:
rowSums() схлопывает каждую строку в одно число (здесь 6 15), colSums() — каждый столбец (5 7 9), а варианты с Means вместо суммы усредняют. Предпочитайте их самописным циклам и даже apply(m, 1, sum) — они понятнее и быстрее. Для сводок, которые эти четыре не покрывают (скажем, максимум по столбцу), общим инструментом служит семейство apply: apply(m, 2, max).
Матрица или датафрейм?
Оба прямоугольны — так что же выбрать?
- Матрица: все ячейки одного типа и важна математика. Численные расчёты, линейная алгебра, вычисление расстояний, сетки вроде изображений. Матрицы легче, а их операции быстрее именно из-за гарантии одного типа.
- Датафрейм: столбцы разных типов — имена рядом с возрастами и логическими флагами. Это реальные табличные данные, и именно их ожидают почти все функции анализа данных.
Хорошее правило: если вы естественным образом открыли бы это в электронной таблице с именованными разнотипными столбцами — это датафрейм. Если это сетка чисел, над которой вы собираетесь делать алгебру, — это матрица. Преобразование между ними несложно (as.matrix(), as.data.frame()), но as.matrix() на датафрейме с любым текстовым столбцом приводит всё к character, поэтому преобразуйте только числовые столбцы.
Что вы уносите с собой
- Матрица — это вектор с размерностями: один тип насквозь, строится через
matrix(data, nrow, ncol)— и заполняется по столбцам, если не передатьbyrow = TRUE. - Индексируйте как
m[row, col]; пустая позиция означает «все»; добавляйтеdrop = FALSE, когда вырезаете одиночные строки или столбцы внутри кода. cbind()иrbind()собирают матрицы из векторов или расширяют существующие.*поэлементное,%*%— настоящее матричное умножение: тот же вход, разные ответы, без предупреждений.rowSums()/colSums()/rowMeans()/colMeans()закрывают повседневные сводки.
Дальше: факторы — как R представляет категориальные данные и какие ловушки с этим приходят.
Часто задаваемые вопросы
Как создать матрицу в R?
matrix(1:6, nrow = 2) перестраивает вектор в 2 строки и 3 столбца, заполняя по столбцам. Добавьте byrow = TRUE, чтобы заполнять по строкам. Матрицу можно также собрать из векторов: cbind() склеивает их как столбцы, rbind() — как строки.
В чём разница между * и %*% в R?
* умножает поэлементно — каждая ячейка на соответствующую ячейку, формы должны совпадать. %*% — настоящее матричное умножение из линейной алгебры (строки на столбцы, поэтому внутренние размерности должны совпадать). На одних и тех же матрицах они дают совершенно разные результаты, и применение * там, где имелось в виду %*%, — классический тихий баг.
Как получить одну строку или столбец матрицы в R?
Оставьте другую позицию пустой: m[1, ] — первая строка, m[, 2] — второй столбец. По умолчанию оба возвращаются как обычные векторы. Добавьте drop = FALSE — как в m[1, , drop = FALSE], — чтобы сохранить результат матрицей из одной строки или одного столбца; это важно, когда дальнейший код ожидает две размерности.