Menu

Векторы в R: c(), seq(), индексация и векторная арифметика

Вектор — фундаментальная единица R, даже одиночное число является вектором. Как создавать их через c(), индексировать (с единицы!), фильтровать логическими масками и считать сразу по всему вектору.

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

Всё в R — вектор

В большинстве языков есть скаляры — одиночные значения — и какой-нибудь контейнерный тип для хранения нескольких. R пропускает скаляр. Вектор — это упорядоченная коллекция значений одного типа, и это та единица, на которой построено всё остальное. Даже то, что выглядит как одинокое число, — вектор длины 1:

c() — сокращение от combine — это то, чем вы собираете вектор вручную. length() сообщает, сколько в нём элементов. А 5 само по себе даёт is.vector равным TRUE и длину 1: R никогда не перестаёт работать с векторами, просто иногда работает с очень короткими.

Из-за этой архитектуры код на R выглядит иначе, чем на Python или JavaScript: операции, для которых в других местах пишут цикл, применяются к целым векторам одним выражением. К этому мы вернёмся ниже.

Одно правило стоит усвоить прямо сейчас: все элементы вектора имеют один тип. Если передать c() смесь, он не пожалуется — он молча приведёт всё к самому гибкому из присутствующих типов:

Строка в смеси превращает всё в строки ("1", "TRUE"). Логические среди чисел становятся 1 и 0. Это приведение типов следует фиксированной иерархии — logical → integer → double → character — и происходит без предупреждения; поэтому затесавшаяся "7" в столбце чисел может тихо превратить весь ваш набор данных в текст. Полная иерархия разобрана в статье про типы данных; если нужно хранить действительно разнородные значения, для этого есть списки.

Генерация последовательностей: :, seq() и rep()

Набирать каждый элемент в c() быстро надоедает. В R есть три сокращения для регулярных шаблонов:

  • 1:10 — повседневный вариант: целые числа от одной границы до другой с шагом 1.
  • seq() обобщает его: задайте шаг через by = или скажите, сколько элементов вам нужно, через length.out =, и пусть R сам вычислит расстояние.
  • seq_len(n) даёт числа от 1 до n и является безопасным способом строить индексы цикла: в отличие от 1:n, он корректно возвращает пустой вектор при n, равном 0, а не считает 1 0 в обратную сторону.
  • rep() повторяет: times = повторяет весь вектор целиком (1 2 1 2 1 2), each = повторяет каждый элемент на месте (1 1 1 2 2 2). Их легко перепутать; запустите сниппет и сравните.

Индексация: R считает с 1

Квадратные скобки извлекают элементы. Первый элемент — [1], а не [0]. Если вы пришли из Python или JavaScript, это самая частая ранняя оплошность:

Три вещи здесь заслуживают паузы:

  • Индексировать можно вектором позиций — fruits[c(2, 4)] берёт второй и четвёртый за один раз.
  • Отрицательные индексы означают «всё, кроме». fruits[-1] — это вектор без первого элемента. Это совершенно не то же самое, что в Python, где -1 означает последний элемент. Смешивать положительные и отрицательные индексы в одном вызове нельзя.
  • fruits[0] — не ошибка; он возвращает пустой character(0). Ошибка на единицу в R часто даёт молчаливо пустой результат, а не падение, поэтому стоит проверять length(), когда что-то дальше по коду загадочно пустует.

Элементы могут также нести имена, что даёт третий способ индексации — по метке:

Именованные векторы работают как лёгкая таблица соответствий и делают код читаемым: prices["tea"] говорит, что имеется в виду, а prices[2] — нет.

Логические маски и which()

Самый мощный способ индексации — логический вектор, маска из значений TRUE/FALSE той же длины, что и данные. Любое сравнение над вектором даёт ровно её:

temps > 24 даёт не один ответ, а пять — по одному на элемент. Поместив эту маску в [ ], вы оставляете элементы там, где маска равна TRUE. Комбинируйте условия через & (и) и | (или) — они разобраны вместе с остальными операторами.

which() переводит маску в позиции: здесь 2 3 5 — индексы жарких измерений. Берите её, когда нужно знать, где находятся совпадения, — чтобы сообщить о них или проиндексировать по тем же местам другой вектор. Для простой фильтрации temps[temps > 24] прямее, чем temps[which(temps > 24)].

Этот шаблон «фильтрация маской» — хребет практически всей работы с данными в R; он возвращается ровно в том же виде, когда вы фильтруете строки датафрейма.

Векторная арифметика и правило переработки

Арифметика в R применяется поэлементно к целым векторам — цикл не нужен:

Первая строка — главная особенность: prices * 2 удваивает каждый элемент. Там, где другим языкам нужен цикл for, R нужен оператор — и векторизованная форма и короче, и быстрее, потому что цикл выполняется в оптимизированном C под капотом. Пишите цикл for, когда каждый шаг зависит от предыдущего; для поэлементной математики векторизуйте.

Третья строка показывает правило переработки: когда длины векторов различаются, R повторяет более короткий, чтобы сравнять их. c(10, 20) перерабатывается в 10 20 10 20, давая 11 22 13 24. Переработка 2 по вектору длины 3 — это ровно то, благодаря чему сработало prices * 2: скаляр — это просто перерабатываемый вектор длины 1.

Переработка чиста, когда большая длина точно кратна меньшей. Когда это не так, R всё равно вычисляет ответ — и выдаёт предупреждение:

Вы получите 11 22 13 плюс предупреждение о том, что длина более длинного объекта не кратна длине более короткого. Относитесь к этому предупреждению как к отчёту об ошибке: частичная переработка почти никогда не является тем, что вы имели в виду, и обычно означает, что два вектора, которые должны были быть одинаковой длины, таковыми не оказались.

Сортировка, разворот, удаление дубликатов

Три маленькие утилиты, которыми вы будете пользоваться постоянно:

sort() упорядочивает значения, rev() переворачивает существующий порядок без сортировки, а unique() отбрасывает дубликаты, сохраняя первые появления. Ни одна из них не изменяет x — как почти всё в R, они возвращают новый вектор и оставляют оригинал в покое.

Что вы уносите с собой

  • Вектор — фундаментальная единица R: упорядоченная, одного типа насквозь, и даже одиночные значения являются векторами длины 1.
  • Стройте через c(), генерируйте шаблоны через :, seq() и rep() — и помните, что c() молча приводит смешанные типы.
  • Индексация начинается с 1; отрицательные индексы выбрасывают элементы; имена позволяют индексировать по метке.
  • Логические маски (x[x > 5]) — идиома фильтрации всего языка; which() превращает маску в позиции.
  • Арифметика векторизована, короткие векторы перерабатываются, а предупреждение о частичной переработке означает, что у вас баг.

Дальше: списки — контейнер для случая, когда ваши значения не все одного типа.

Часто задаваемые вопросы

Как создать вектор в R?

С помощью функции c() (от combine): x <- c(10, 20, 30). Для регулярных последовательностей используйте 1:10, seq(0, 1, by = 0.25) или rep(0, 5). Все элементы в итоге должны быть одного типа — если смешать типы, R молча приведёт их все к самому гибкому.

Индексация в R начинается с 0 или с 1?

С 1. x[1] — первый элемент, x[length(x)] — последний. Это сбивает с толку всех, кто пришёл из Python, JavaScript или C: элемента x[0] не существует (запрос вернёт пустой вектор, а не ошибку, из-за чего баг остаётся тихим).

Что делает правило переработки в R?

Когда вы выполняете арифметику над двумя векторами разной длины, R повторяет более короткий, пока он не сравняется с длинным. c(1, 2, 3, 4) + c(10, 20) даёт 11 22 13 24. Если большая длина не кратна меньшей, R всё равно вычислит ответ, но выдаст предупреждение — относитесь к нему как к сообщению об ошибке.

Что делает which() в R?

Она превращает логический вектор в позиции его значений TRUE. Если x > 5 даёт FALSE TRUE TRUE, то which(x > 5) даёт 2 3. Используйте её, когда нужны сами позиции; для простой фильтрации проще индексировать логическим вектором напрямую (x[x > 5]).

Coddy programming languages illustration

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

НАЧАТЬ