2  Введение в R

Автор

И.С. Поздняков

2.1 Установка R и Rstudio

Для работы с R необходимо его сначала скачать и установить.

  • R
    • на Windows, найдите большую кнопку Download R (номер версии) for Windows.
    • на Mac, если маку меньше, чем 5 лет, то смело ставьте *.pkg файл с последней версией. Если старше, то поищите на той же странице версию для вашей системы.
    • на Linux, также можно добавить зеркало и установить из командной строки:
sudo apt-get install r-cran-base

В данной книге используется следующая версия R:

sessionInfo()$R.version$version.string
[1] "R version 4.2.2 (2022-10-31)"

После установки R необходимо скачать и установить RStudio:

Если вдруг что-то установить не получается (или же вы просто не хотите устанавливать на компьютер лишние программы), то можно работать в облаке, делая все то же самое в веб-браузере:

Первый и вполне закономерный вопрос: зачем мы ставили R и отдельно еще какой-то RStudio? Если опустить незначительные детали, то R – это сам язык программирования, а RStudio – это интегрированная среда разработки (integrated development environment; IDE), которая позволяет в этом языке очень удобно работать.

RStudio – это не единственная среда для R, но, определенно, самая удобная на сегодняшний день. Почти все пользуются именно ей и не стоит тратить время на поиск чего-то более удобного и лучшего. Если же вы привыкли работать с Jupyter Notebook, то в R обычно вместо него используются великолепный R Markdown или еще более великолепный Quarto – с помощью последнего и написан этот онлайн-учебник, кстати говоря. И с R Markdown и Quarto мы тоже будем разбираться (см. Глава 16)!

2.2 Знакомство с RStudio

Так, давайте взглянем на то, что нам тут открылось:

В первую очередь нас интересуют два окна: 1 - Code Editor (окно для написания скриптов)1 и 2 - R Console (консоль). Здесь можно писать команды и запускать их. При этом работа в консоли и работа со скриптом немного различается.

В 2 - R Console вы пишите команду и запускаете ее нажиманием Enter. Иногда после запуска команды появляется какой-то результат. Если нажимать стрелку вверх на клавиатуре, то можно выводить в консоль предыдущие команды. Это очень удобно для запуска предыдущих команд с небольшими изменениями.

В 1 - Code Editor для запуска команды вы должны выделить ее и нажать Ctrl + Enter (Cmd + Enter на macOS). Если не нажать эту комбинацию клавиш, то команда не запустится. Можно выделить и запустить сразу несколько команд или даже все команды скрипта. Все команды скрипта можно выделить с помощью сочетания клавиш Ctrl + A на Windows и Linux, Cmd + A на macOS 2. Как только вы запустите команду (или несколько команд), соответствующие строчки кода появятся в 2 - R Console, как будто бы вы запускали их прямо там.

Обычно в консоли удобно что-то писать, чтобы быстро что-то посчитать. Скрипты удобнее при работе с длинными командами и как способ сохранения написанного кода для дальнейшей работы. Для сохранения скрипта нажмите File - Save As.... R скрипты сохраняются с разрешением .R, но по своей сути это просто текстовые файлы, которые можно открыть и модифицировать в любом текстовом редакторе а-ля “Блокнот”.

3 - Workspace and History – здесь можно увидеть переменные. Это поле будет автоматически обновляться по мере того, как Вы будете запускать строчки кода и создавать новые переменные. Еще там есть вкладка с историей всех команд, которые были запущены.

4 - Plots and files. Здесь есть очень много всего. Во-первых, небольшой файловый менеджер, во-вторых, там будут появляться графики, когда вы будете их рисовать. Там же есть вкладка с вашими пакетами (Packages) и Help по функциям. Но об этом потом.

2.3 R как калькулятор

R – полноценный язык программирования, который позволяет решать широкий спектр задач. Но в первую очередь R используется для анализа данных и статистических вычислений. Тем не менее, многими R до сих пор воспринимается как просто продвинутый калькулятор. Ну что ж, калькулятор, так калькулятор.

Давайте начнем с самого простого и попробуем использовать R как калькулятор с помощью арифметических операторов +, -, *, /, ^ (степень), () и т.д.

Просто запускайте в консоли пока не надоест:

40 + 2
[1] 42
3 - 2
[1] 1
5 * 6
[1] 30
99 / 9 #деление
[1] 11
2 ^ 3 #степень, 2 ** 3 тоже работает
[1] 8
13 %/% 3 #целочисленное деление
[1] 4
13 %% 3 #остаток от деления
[1] 1
Время мемов

Попробуйте самостоятельно посчитать что-нибудь с разными числами.

Ничего сложного, верно? Вводим выражение и получаем результат.

Полезное: комментарии

Вы могли заметить, что некоторые команды у меня заканчиваются знаком решетки (#). Все, что написано в строчке после # игнорируется R при выполнении команды. Написанные команды в скрипте рекомендуется сопровождать комментариями, которые будут объяснять вам же в будущем (или кому-то еще), что конкретно происходит в соответствующем куске кода 3. Кроме того, комментарии можно использовать в тех случаях, когда вы хотите написать кусок кода по-другому, не стирая полностью предыдущий код: достаточно “закомментить” нужные строчки - поставить # в начало каждой строки, которую вы хотите переписать. Для этого есть специальное сочетание горячих клавиш: Ctrl + Shift + C (Cmd + Shift + C на macOS) – во всех выделенных строчках будет написан # в начале.

Согласно данным навязчивых рекламных баннеров в интернете, только 14% россиян могут справиться с этим примером:

2 + 2 * 2
[1] 6

На самом деле, разные языки программирования ведут себя по-разному в таких ситуациях, поэтому ответ 6 (сначала умножаем, потом складываем) не так очевиден.

Порядок выполнения арифметических операций (т.е. приоритет операторов, operator precedence) в R как в математике, так что не забывайте про скобочки.

(2 + 2) * 2
[1] 8

Если Вы не уверены в том, какие операторы имеют приоритет, то используйте скобочки, чтобы точно обозначить, в каком порядке нужно производить операции. Или же смотрите на таблицу приоритета операторов с помощью команды ?Syntax.

2.4 Функции

Давайте теперь извлечем корень из какого-нибудь числа. В принципе, тем, кто помнит школьный курс математики, возведения в степень вполне достаточно:

16 ^ 0.5
[1] 4

Ну а если нет, то можете воспользоваться специальной функцией: это обычно какие-то буквенные символы с круглыми скобками сразу после названия функции. Мы подаем на вход (внутрь скобочек) какие-то данные, внутри этих функций происходят какие-то вычисления, которые выдает в ответ какие-то другие данные (или же функция записывает файл, рисует график и т.д.).

Вот, например, функция для корня:

sqrt(16)
[1] 4
Осторожно!

R – case-sensitive язык, т.е. регистр важен. SQRT(16) не будет работать.

А вот так выглядит функция логарифма:

log(8)
[1] 2.079442

Так, вроде бы все нормально, но… Если Вы еще что-то помните из школьной математики, то должны понимать, что что-то здесь не так.

Здесь не хватает основания логарифма!

Логарифм – показатель степени, в которую надо возвести число, называемое основанием, чтобы получить данное число.

То есть у логарифма 8 по основанию 2 будет значение 3:

\(\log_2 8 = 3\)

То есть если возвести 2 в степень 3 у нас будет 8:

\(2^3 = 8\)

Только наша функция считает все как-то не так.

Чтобы понять, что происходит, нам нужно залезть в хэлп этой функции:

?log

Справа внизу в RStudio появится вот такое окно, в котором можно прочитать документацию (documentation) по выбранной функции:

Полезное: Документация в R

Документация в R есть для всех встроенных функций. Еще есть документация для встроенных наборов данных и других объектов, но большая часть документации касается именно функций. Документация всегда имеет одинаковую структуру, которая позволяет быстро понять, что функция требует на входе, что она делает и что возвращает. Подробнее про структуру документации можно почитать здесь.

В верхней части документации (Usage) функция прописана со всеми используемыми аргументами (arguments). Если через знак = для этих аргументов что-то прописано, то это означает, что эти параметры имеют значения по умолчанию.

Действительно, у функции log() есть еще аргумент base =. По умолчанию он равен числу Эйлера (2.7182818…), т.е. функция считает натуральный логарифм. В большинстве функций R есть какой-то основной аргумент – данные в том или ином формате, а есть и дополнительные аргументы, которые можно прописывать вручную, если значения по умолчанию вас не устраивают. Обычно эти дополнительные аргументы – это параметры функции, которые задают то, как именно функция работает.

log(x = 8, base = 2)
[1] 3

…или просто (если вы уверены в порядке переменных):

log(8, 2)
[1] 3

Более того, вы можете использовать результат выполнения одних функций в качестве аргумента для других:

log(8, sqrt(4))
[1] 3

Если эксплицитно писать имена аргументов, то их порядок в функции не важен:

log(base = 2, x = 8)
[1] 3

А еще можно писать имена аргументов не полностью, если они не совпадают с другими:

log(b = 2, x = 8)
[1] 3

Мы еще много раз будем возвращаться к функциям. Вообще, функции – это одна из важнейших штук в R (примерно так же как и в Python). Мы будем создавать свои функции, использовать функции как аргументы для функций и многое-многое другое. В R очень крутые возможности работы с функциями. Поэтому подружитесь с функциями, они клевые.

Для продвинутых: операторы – это тоже функции

Арифметические знаки, которые мы использовали: +,-,/,^ и т.д. называются операторами и на самом деле тоже являются функциями:

`+`(3, 4)
[1] 7

Это не очень читаемая запись, конечно, поэтому использовать так обычно не рекомендуется: мы хотим, чтобы код был максимально понятным. Но ведь любопытно, правда?

2.5 В любой непонятной ситуации – гуглите

Если вдруг вы не знаете, что искать в хэлпе, или хэлпа попросту недостаточно, то… гуглите!

Время мемов

Нет ничего постыдного в том, чтобы гуглить решения проблем. Это абсолютно нормально. Используйте силу интернета во благо и да помогут вам Stackoverflow4 и бесчисленные R-туториалы!

Время мемов

Главное, помните: загуглить работающий ответ всегда недостаточно. Надо понять, как и почему решение работает. Иначе что-то обязательно пойдет не так.

Кроме того, правильно загуглить проблему – не так уж и просто. Это отдельное искусство, которое необходимо освоить любому, кто занимается программированием: у меня быстро находить ответ на вопрос в интернете. Это не шутка!

Время мемов

Короче говоря:

Гуглить – хорошо, бездумно копировать чужие решения – плохо.

2.6 Переменные

Важная штука в программировании на практически любом языке – возможность сохранять значения в переменных (variables). В R это обычно делается с помощью вот этих символов: <- (но можно использовать и обычное =, хотя это не очень принято). Для этого есть удобное сочетание клавиш: нажмите одновременно Alt + - (или option + - в macOS).

a <- 2
Полезное: присвоение и вывод в консоль

Заметьте, при присвоении результат вычисления не выводится в консоль! Если опустить детали, то обычно результат выполнения команды либо выводится в консоль (и не сохраняется), либо записывается в переменную (но тогда не показывается в консоли).

Чтобы вывести в консоль содержание существующей переменной, просто введите ее название:

a
[1] 2

Справа от <- находится значение, которое вы хотите сохранить, или же какое-то выражение, результат которого вы хотите сохранить в эту переменную5:

Слева от <- находится название будущей переменной. Название переменных может быть самым разным.

Advanced: синтаксически валидные имена для переменных

Есть несколько ограничений для синтаксически валидных имен переменных: они должны включать в себя буквы, цифры, . или _, начинаться на букву (или точку, за которой не будет следовать цифра), не должны совпадать с коротким списком зарезервированных слов. Короче говоря, название не должно включать в себя пробелы и большинство других знаков.

Нельзя: - new variable - _new_variable - .1var - v-r

Можно: - new_variable - .new.variable - var_2

На самом деле, можно создавать переменные с невалидными именами. Такие названия нужно обрамлять бэктиками (`):

`1 СЛОВНО ...` <- 100
`1 СЛОВНО ...`
[1] 100

Это же относится и к другим именам в R, например, к названиям колонок датафрейма (см. Глава 4.4), но таких имен лучше, конечно, избегать.

Полезное: понятные имена переменных

Обязательно делайте названия переменных осмысленными! Старайтесь делать при этом их понятными и короткими, это сохранит вам очень много времени, когда вы (или кто-то еще) будете пытаться разобраться в написанном ранее коде. Если название все-таки получается длинным и состоящим из нескольких слов, то лучше всего использовать нижнее подчеркивание в качестве разделителя: some_variable6.

После присвоения переменная появляется во вкладке Environment в RStudio:

Теперь использовать переменные в функциях и просто вычислениях:

b <- a ^ a + a * a
b
[1] 8
log(b, a)
[1] 3

Удалять переменные можно с помощью функции rm():

e <- a ^ b
e
[1] 256
rm(e)
e #переменной больше нет, возвращает ошибку
Error in eval(expr, envir, enclos): object 'e' not found

2.7 Логические операторы

Вы можете сравнивать разные переменные:

a == b
[1] FALSE

Заметьте, что сравнивая две переменные мы используем два знака равно ==, а не один =. Иначе это будет означать присвоение.

a = b
a
[1] 8
Время мемов

Теперь Вы сможете понять комикс про восстание роботов на следующей странице (пусть он и совсем про другой язык программирования)

Этот комикс объясняет, как важно не путать присваивание и сравнение (хотя я иногда путаю до сих пор =( ).

Иногда нам нужно проверить на неравенство:

a <- 2
b <- 3

a == b
[1] FALSE
a != b
[1] TRUE

Восклицательный язык в программировании вообще и в R в частности стандартно означает отрицание.

Еще мы можем сравнивать на больше/меньше:

a > b
[1] FALSE
a < b
[1] TRUE
a >= b
[1] FALSE
a <= b
[1] TRUE

Этим мы будем пользоваться в дальнейшем регулярно! Именно на таких простых логических операциях построено большинство операций с данными.

2.8 Типы данных

2.8.1 Числовые типы

До этого момента мы работали только с числами (numeric). На самом деле, в R три типа numeric: integer (целые), double (дробные), complex (комплексные числа)7. R сам будет конвертировать числа в нужный числовой тип при необходимости, поэтому этим можно не заморачиваться за исключением редких случаев.

Если же все-таки нужно задать конкретный тип числа эксплицитно, то можно воспользоваться функциями as.integer(), as.double() и as.complex().

Для продвинутых: числа не то, чем кажутся

Если число выглядит как целое, еще не факт, что это integer. Внутри оно может храниться как дробное, но, как я уже написал, это обычно не так важно. Но если очень нужно сделать именно integer, при создании числа можно поставить в конце L:

is.integer(5)
[1] FALSE
is.integer(5L)
[1] TRUE
Для продвинутых: ограничения дробных чисел

Про double есть еще один маленький секрет. Дело в том, что дробные числа хранятся в R как числа с плавающей запятой двойной точности (отсюда и название – “double”, т.е. “double precision”). Да-да, компьютеры неточные! Дробные числа в компьютере могут быть записаны только с определенной степенью точности, поэтому иногда встречаются вот такие вот ситуации:

sqrt(2) ^ 2 == 2
[1] FALSE

Казалось бы, с обоих сторон – 2, почему же тогда FALSE? Дело в том, что в обоих случаях это дробное число, которое не может равняться ровно двум. Внутри это не два, а что-то вроде 2.0000….001 или 1.9999…999, число записано как степени двойки и отображено в десятичной системе. R не показывает все доступные цифры дробной части ради удобства, поэтому показывает округление, и мы видим просто число 2.

Поэтому при сравнении двух чисел происходит не сравнение их разницы с чистым круглым нулем, а сопоставление с очень маленьким числом – машинной ошибкой или машинным эпсилоном (machine epsilon). Если разница между двумя дробными числами меньше этого эпсилона, то числа считаются равными. Если выходят за пределы этой ошибки, то нет. Размер машинного эпсилона можно посмотреть с помощью .Machine$double.eps.

Обычно это все работает хорошо, но некоторые операции “выпрыгивают” за рамки этой ошибки, отсюда и такое “странное” поведение, которое иногда может возникать при сравнении двух чисел. Если хотите углубиться в этот вопрос, то можете почитать здесь.

Это довольно стандартная ситуация, характерная не только для R. Чтобы ее избежать, можно воспользоваться функцией all.equal():

all.equal(sqrt(2) ^ 2, 2)
[1] TRUE

Еще один пример необычного поведения дробных чисел, связанный с их “неточностью”, можно наблюдать в ситуациях, где вы ожидаете ноль, а получаете что-то такое:

sin(pi)
[1] 1.224647e-16

По правилам тригонометрии, \(sin(\pi) = 0\), тогда откуда у нас это странное число с e-16 в конце?

Это то, что называется экспоненциальной записью (scientific notation) числа, которую R использует автоматически, если нужно напечатать очень маленькое или очень большое число. Экспоненциальная запись часто используется как в компьютерах, так и во многих науках, так что читать ее полезно уметь.

Если перед нами очень маленькое число, то вместо того, чтобы писать многочисленные нули, можно записать его как \(m\times 10^{n}\) , где \(m\) – число от 1 до 10, а \(n\) – это отрицательная степень десяти: \(10^{-1} = 0.1\), \(10^{-2} = 0.01\), \(10^{-3} = 0.001\), … , \(10^{-16} = 0.0000000000000001\).

е-16 в конце числа – это и есть $10^{-16}$, а 1.224647 – это множитель \(m\), на который полученное число с очень большим количеством нулей умножается (точнее, только его первые несколько цифр). Получается, что этот множитель не сильно меняет погоды, поэтому когда видите число с е-, нужно смотреть в первую очередь именно на степень. Вот так это число выглядит в родной для нас десятичной записи:

format(sin(pi), scientific = FALSE)
[1] "0.0000000000000001224647"

Эта маленькая дробная часть возникла из-за этой “неточности” хранения дробных чисел и самого числа \(\pi\) в компьютере, которую мы обсуждали выше.

Аналогично маленьким числам, R использует экспоненциальную запись, когда нужно напечатать очень большое число:

2 ^ 40
[1] 1.099512e+12

Обратите внимание, что здесь у нас e+12, а не e-12, что означает $10^{12}$, а не $10^{-12}$. То есть перед нами теперь \(1.099512\times 10^{12}\):

format(2 ^ 40, scientific = FALSE)
[1] "1099511627776"

Опять же, 1.099512 – это только первые цифры, в экспоненциальной записи R не показывает их все. Смотреть нужно сначала на степень (e+12), только потом – на множитель впереди.

2.8.2 Строковый тип

Строковые (character) данные – это набор букв, цифр и символов. Чтобы создать строковую переменную, нужные знаки обособляются кавычками.

s <- "Всем привет!"
s
[1] "Всем привет!"
class(s)
[1] "character"

Как и в Python, можно использовать как ", так и ' (что удобно, когда строчка внутри уже содержит какие-то кавычки).

"Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn"
[1] "Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn"

Главное, открывать и закрывать кавычки одинаковыми кавычками ( " или ')

Чтобы соединить несколько строковых переменных вместе, можно воспользоваться функцией paste()8:

paste("I", "love", "R")
[1] "I love R"

По умолчанию функция paste() соединяет строки пробелами, но разделитель можно настроить параметром sep =:

paste("I", "love", "R", sep = "_<3_")
[1] "I_<3_love_<3_R"

Чтобы соединять строки, так сказать, “без ничего”, нужно поставить sep = "" или же воспользоваться функцией paste0() специально для этого конкретного случая:

paste("I", "love", "R", sep = "")
[1] "IloveR"
paste0("I", "love", "R")
[1] "IloveR"

2.8.3 Логический тип

Логические (logical) данные – это просто TRUE или FALSE. То же самое, что и boolean тип в других языках программирования

t1 <- TRUE
f1 <- FALSE

t1
[1] TRUE
f1
[1] FALSE
Для продвинутых: T и F

Вообще, можно еще писать T и F (но не True и False!)

t1 <- T
f1 <- F

Это плохая практика: R защищает от перезаписи переменные TRUE и FALSE, но не защищает от этого T и F.

TRUE <- FALSE
Error in TRUE <- FALSE: invalid (do_set) left-hand side to assignment
TRUE
[1] TRUE
T <- FALSE
T
[1] FALSE

Теперь удалим эту переменную T от греха подальше иначе все перестанет работать при ее использовании:

rm(T)

Мы уже встречались с логическими значениями при сравнении двух числовых переменных. Теперь вы можете догадаться, что результаты сравнения, например, числовых или строковых переменных, можно тоже сохранять в переменные!

comparison <- a == b
comparison
[1] FALSE

Это нам очень понадобится, когда мы будем работать с реальными данными: нам нужно будет постоянно вытаскивать какие-то данные из датасета, что как раз и построено на игре со сравнением переменных.

Чтобы этим хорошо уметь пользоваться, нам нужно еще освоить как работать с логическими операторами. Про один мы немного уже говорили – это логическое НЕ (!). ! превращает TRUE в FALSE, а FALSE в TRUE:

t1
[1] TRUE
!t1
[1] FALSE
!!t1 #Двойное отрицание!
[1] TRUE

Еще есть логическое И (выдаст TRUE только в том случае если обе переменные TRUE):

t1 & t1
[1] TRUE
t1 & f1
[1] FALSE
f1 & t1
[1] FALSE
f1 & f1
[1] FALSE

А еще логическое ИЛИ (выдаст TRUE в случае если хотя бы одна из переменных TRUE):

t1 | t1
[1] TRUE
t1 | f1
[1] TRUE
f1 | t1
[1] TRUE
f1 | f1
[1] FALSE

Обратите внимание: t1 | t1, то есть когда с обоих сторон TRUE, тоже возвращает TRUE!

Для продвинутых: строгое ЛИБО

Если кому-то вдруг понадобится другое ИЛИ (строгое ЛИБО) – есть функция xor(), принимающая два аргумента и возвращая TRUE только в том случае, если ровно один из двух аргументов равен TRUE. Но на практике она нужна очень редко, тогда как логические И и ИЛИ нужны очень часто: например, вам нужно отобрать только респондентов от 18 до 25, для этого нужно сделать два сравнения: что возраст больше 18 и что возраст меньше 25, после чего соединить два сравнения логическим И.

Итак, мы только что разобрались с самой занудной (хотя и важной) частью - с основными типа данных в R и как с ними работать9. Пора переходить к чему-то более интересному и специфическому для R. Вперед к ВЕКТОРАМ!


  1. При первом запуске RStudio вы не увидите это окно. Для того, чтобы оно появилось, нужно нажать File - New File - R Script.↩︎

  2. В RStudio есть много удобных сочетаний горячих клавиш. Чтобы посмотреть их все, нажмите Help - Keyboard Shortcuts Help.↩︎

  3. Во время написания кода вам может казаться понятным то, что вы написали, но при возвращении к коду через некоторое время вы уже не будете этого помнить. Старайтесь писать комментарии как можно чаще!↩︎

  4. Stackoverflow – это сайт с вопросами и ответами. Эдакий аналог Quora, The Question, ну или Ответы Mail.ru в мире программирования.↩︎

  5. Есть еще оператор ->, который позволяет присваивать значения слева направо, но так делать не рекомендуется, хотя это бывает довольно удобным.↩︎

  6. Еще иногда используются большие буквы SomeVariable, но это плохо читается, а иногда – точка, но это тоже не рекомендуется.↩︎

  7. Комплексные числа в R пишутся так: complexnumber <- 2+2i. i здесь - это та самая мнимая единица, которая является квадратным корнем из -1.↩︎

  8. Оператор +, в отличие от Python, не работает для соединения строк.↩︎

  9. Кроме описанных пяти типов данных (integer, double, complex, character и logical) есть еще и шестой – это raw, сырая последовательность байтов, но нам она не понадобится.↩︎