Menu

Матрицы в R: matrix(), cbind, rbind и матричная арифметика

Как строить матрицы через matrix(), cbind() и rbind(), индексировать строки и столбцы и не путать поэлементное * с настоящим матричным умножением %*%.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Матрица — это вектор с размерностями

Матрица в 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], — чтобы сохранить результат матрицей из одной строки или одного столбца; это важно, когда дальнейший код ожидает две размерности.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ