Menu

Числа в R: numeric, integer и математические функции

Работа с числами в R: numeric против integer, семейство функций округления, sqrt и log, оператор остатка %% и специальные значения Inf и NaN.

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

Numeric и integer: два числовых типа R

R хранит числа двумя способами. Numeric (хранится как число с плавающей точкой двойной точности) — тип по умолчанию: любое набранное вами число является double, если вы не сказали иначе. Integer — отдельный точный целочисленный тип, который запрашивают суффиксом L:

На практике вам редко приходится об этом думать: R преобразует их молча, а is.numeric() даёт TRUE для обоих (полная система типов — в статье про типы данных). Единственное место, где различие проступает наружу, — деление: / всегда возвращает double, даже между двумя целыми:

Целые числа в основном появляются как результат подсчитывающих функций (length(), seq_len(), nrow()) и как значения индексов. Когда нужно целочисленное деление, у R есть отдельный оператор — о нём ниже.

Семейство округления

R даёт пять способов подрезать число, у каждого свой смысл:

  • round(x, digits) — до заданного числа знаков после запятой (по умолчанию 0).
  • floor(x) — вниз до ближайшего целого, всегда в сторону минус бесконечности.
  • ceiling(x) — вверх, всегда в сторону плюс бесконечности.
  • trunc(x) — отсекает дробную часть, всегда в сторону нуля. Обратите внимание на разницу для отрицательных: trunc(-2.7) это -2, а floor(-2.7) это -3.
  • signif(x, digits) — до заданного числа значащих цифр, а не знаков после запятой: signif(123456, 2) это 120000.

И один знаменитый сюрприз: round() для точных половин использует округление к ближайшему чётному (банковское), по стандарту IEEE 754:

Это напечатает 0 2 2 4 — каждая половина округляется к ближайшему чётному числу. Это не баг; так предотвращается систематическое смещение вверх при суммировании множества округлённых значений. Если отчёту нужно школьное округление, добавьте крошечный сдвиг или форматируйте на уровне представления.

Повседневные математические функции

Базовые вещи работают ровно так, как вы и предполагаете:

sqrt() — квадратный корень, abs() — модуль, ^ — возведение в степень, а exp(x) — это e в степени x, так что exp(1) — число Эйлера, примерно 2.718282.

Единственная функция, на которой все спотыкаются, — log(). В R log() — это натуральный логарифм (по основанию e), а не по основанию 10:

Первая строка печатает примерно 4.60517 — а не 2, которого ждёт читатель, привыкший к основанию 10. Берите log10() и log2(), когда имеете в виду эти основания, или передавайте base = явно. (Это соглашение стандартно для статистики, где натуральный логарифм — вариант по умолчанию.)

Остаток %% и целочисленное деление %/%

Два оператора закрывают арифметику остатков:

%% — это остаток от деления: 17 делить на 5 будет 3 и 2 в остатке. %/%целочисленное деление: сколько целых пятёрок помещается в 17. Вместе они удовлетворяют равенству x == (x %/% y) * y + (x %% y).

Классическое применение %% — проверка делимости:

Одна тонкость с отрицательными числами: %% в R берёт знак делителя (как в Python, в отличие от C):

Это 2, а не -1 — R отвечает на вопрос «что нужно прибавить к кратному трём, чтобы получить -7?», и результат остаётся в диапазоне 0..2 для положительного делителя. Удобно для циклического обхода индексов; неожиданно, если вы пришли из C или Java.

Специальные значения: Inf, -Inf и NaN

Числа R следуют стандарту IEEE 754, поэтому некоторые операции дают специальные значения вместо ошибок:

1/0 это Inf (бесконечность), -1/0 это -Inf, а 0/0 — по-настоящему неопределённая величина — это NaN, «не число». Различие важно: Inf — это ответ («больше чего угодно»), NaN — его отсутствие. Проверяют их специальными функциями, потому что == NaN никогда не работает:

Обратите внимание на последнюю строку: NaN также считается NA, поэтому is.na() его ловит — ещё одна причина, по которой is.na() служит стандартной проверкой «пригодно ли это значение?» (подробнее в статье про пропущенные значения).

R также читает и пишет научную нотацию нативно: 2.5e3 это 2500, а очень маленькие и очень большие числа по умолчанию печатаются в e-нотации:

Используйте format(x, scientific = FALSE) (или опцию scipen), когда отчёту нужны обычные десятичные числа.

Сюрприз плавающей точки

Он общий для всех языков, хранящих дроби в двоичном виде, и R не исключение:

FALSE — потому что 0.1 + 0.2 на самом деле равно 0.30000000000000004. Ни у 0.1, ни у 0.2 нет точного двоичного представления, и крошечные погрешности накапливаются. Печать R по умолчанию скрывает это, показывая 7 значащих цифр, поэтому проблема кажется невидимой, пока не подведёт сравнение через ==.

Правило: никогда не сравнивайте вычисленные дробные числа через ==. Используйте all.equal(), которая сравнивает с разумным допуском:

Оборачивайте её в isTRUE(), потому что при различии значений all.equal() возвращает описание различия, а не FALSE. Для работы с целыми числами, где важна точность, integer точен примерно до 2,1 миллиарда — ещё одна причина, почему подсчитывающий код использует целочисленный тип.

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

  • Любое набранное число — это double; 42L создаёт integer, а / в любом случае возвращает double.
  • round() округляет половины к чётному; floor/ceiling/trunc/signif подрезают по-разному — знайте, что именно вам нужно.
  • log()натуральный логарифм; для других оснований используйте log10(), log2() или base =.
  • %% даёт остаток (знак следует за делителем), %/% — целую часть частного.
  • 1/0 это Inf, 0/0 это NaN, а 0.1 + 0.2 != 0.3 — сравнивайте дроби через all.equal(), никогда через ==.

Дальше: вторая половина повседневных данных — строки и функции, которые R даёт для их создания, форматирования и поиска.

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

В чём разница между numeric и integer в R?

Numeric (double) — тип по умолчанию для любого набранного числа: 42 это double, хотя и выглядит целым. Integer — отдельный тип хранения, который запрашивают суффиксом L: 42L. Обычное деление всегда возвращает double, даже между целыми; для целочисленного деления используйте %/%.

log() в R — это натуральный логарифм?

Да — log(x) в R это натуральный логарифм (по основанию e), а не по основанию 10. Для основания 10 используйте log10(), для 2 — log2(), а для произвольного основания — log(x, base = b). log(100) это примерно 4.605, а не 2.

Как работает round() в R?

round(x, digits) округляет до заданного числа знаков после запятой, но точные половины округляются «к ближайшему чётному» (банковское округление): round(2.5) это 2, а round(3.5) это 4. Так предписывает стандарт IEEE 754, и это снижает смещение при суммировании округлённых значений, но удивляет тех, кто ждёт школьного округления.

Почему 0.1 + 0.2 не равно 0.3 в R?

Числа double хранятся в двоичном виде, а у 0.1, 0.2 и 0.3 нет точного двоичного представления, поэтому 0.1 + 0.2 на самом деле равно 0.30000000000000004. Никогда не сравнивайте вычисленные дробные числа через ==; используйте isTRUE(all.equal(x, y)) или проверяйте abs(x - y) < 1e-9.

Coddy programming languages illustration

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

НАЧАТЬ