Основы R, типы данных


Язык программирования R

R - язык программирования и программная среда предназначенная для статистических вычислений. Получил широкое распространение в среде разработки статистического программного обеспечения, анализе данных и как средство для построения графиков.

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

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

Является open-source реализацией языка S, разработанного в Bell Labs.

Недостатками R являются нетривиальный синтаксис, который требует привыкания и относительно медленная скорость работы.

Основы

R доступен для всех современных операционных систем, и поставляется с полноценной REPL средой. Интерактивную консоль можно использовать для простых экспериментов, в качестве калькулятора и для чтения документации.

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

В разных style guide-ах можно найти противоположные рекомендации по использованию операторов присваивания, но в сообществе R разработчиков более распространен первый вариант <-, его я и буду использовать в данном цикле статей.

#  Комментарии начинаются со знака решетки

#  Создадим нескольких переменных
> width <- 5
> height <- 4

# Используем наши новые переменные
> square <- width * height
> perimeter <- 2 * (height + width)
> square
[1] 20
> perimeter
[1] 18

# Функция "ls" показывает переменные в текущем окружении
> ls()
[1] "height" "width" "square" "perimeter"

# Функция "rm" удаляет переменную из окружения
> rm(perimeter)
> ls()
[1] "height" "width" "square"

Для полноценных программ исходный код сохраняется в R-скрипты, файлы с расширением .R.

# Функция "cat" выводит строчку в консоль или файл
cat("Compute the square of pizza:\n")
pizza_radius <- 3
pizza_square <- pi * pizza_radius ^ 2
cat(pizza_square)

Исполняется такой скрипт командой Rscript square.R. Или можно просто поставить IDE, например RStudio, open-source версия которой доступна для всех платформ.

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

> ?sum
Sum of Vector Elements

Description
sum returns the sum of all the values present in its arguments.

Usage
sum(..., na.rm = FALSE)
Arguments

...
numeric or complex or logical vectors.
na.rm   
logical. Should missing values (including NaN) be removed?
Details

This is a generic function: methods can be defined for it directly or via the Summary group generic. For this to work properly, the arguments ... should be unnamed, and dispatch is on the first argument.

Или поискать название функции в интернете.

Logical

Самым простым является логический тип данных logical. К нему относятся значения TRUE и FALSE, можно просто T и F. Еще logical соответствует значение NA, но сейчас это неважно.

Чтобы определить к какому типу относится переменная есть функция class(), которая принимает переменную и возвращает ее тип.

> variable <- FALSE
> class(variable)
[1] "logical"

> class(TRUE)
[1] "logical"

Для logical переменных определены основные логические операции.

> TRUE && (FALSE || TRUE)
[1] TRUE

> xor(TRUE, FALSE)
[1] TRUE

> !TRUE
[1] FALSE

Numeric

Числовой тип данных представлен numeric и поддерживает основные арифметические операции.

# Ничего интересного
> 1984
[1] 1984

> 2.7118
[1] 2.7118

> 3 + 8.4
[1] 11.4

# Возведение в степень
> 7 ^ 4
[1] 2401

# Остаток деления, mod
> 9 %% 2
[1] 1

# Целочисленное деление, div
> 9 %/% 2
[1] 4

Частным случаем numeric является целочисленный тип integer. Для указания что число является integer к нему дописывается L.

# Это numeric
> class(13)
[1] "numeric"

# А это integer
> class(13L)
[1] "integer"

Все integer являются numeric, но не все numeric являются integer.

Character

Для хранения строк есть character, тут все просто.

> "Hello, World!"
[1] "Hello World!"

# Конкатенация строк производится функцией "paste", "+" не работает для строк!
> name <- "John"
> surname <- "Galt"
> character <- paste(name, surname)
character
[1] "John Galt"

# Печатаем строку
> sprintf("Who is %s?", character)
[1] "Who is John Gult?"

# Получаем подстроку
> substr("Answer to the Ultimate Question of Life, the Universe, and Everything", start = 23, stop = 39)
[1] " Question of Life"

# Заменяем подстроку
> sub("problem", "dream!", "I have a problem")
[1] "I have a dream!"

Complex

Тип данных для операций с комплексными числами:

# Создаем комплексное число
> z <- 2 + 1i
> z
[1] 2+1i

# Действительная часть
> Re(z)
[1] 2

# Мнимая часть
> Im(z)
[1] 1

# Модуль
> Mod(z)
[1] 2.236068

# Аргумент

> Arg(z)
[1] 0.4636476

# Сопряженное комплексное число
> Conj(z)
[1] 2-1i

Определяем и конвертируем типы

Чтобы проверить, принадлежит ли переменная к какому-либо типу можно использовать функции вида is.*:

# Возвращаясь к numeric и integer
> is.numeric(5)
[1] TRUE

> is.numeric(5L)
[1] TRUE

> is.integer(3)
[1] FALSE

> is.integer(3L)
[1] TRUE

# Другие типы
> is.character("Mew!")
[1] TRUE

> is.logical(FALSE)
[1] TRUE

Для конвертации типов используются функции вида as.*:

# logical в numeric
> as.numeric(TRUE)
[1] 1

> as.numeric(FALSE)
[1] 0

# numeric в character
> as.character(8)
[1] "8"

# character в numeric
> as.numeric("4.2")
[1] 4.2

# character в integer
> as.integer("4.2")
[1] 4

# а так делать нельзя
> as.numeric("This is not a number!")
[1] NA
Warning message:
NAs introduced by coercion

Векторы

Вектор в R это набор элементов одного типа. Создаются векторы функцией c(), сокращение от combine.

# Вектор чисел

> remain <- c(10, 11, 13, 8)
> remain
[1] 10 11 13 8

> is.vector(remain)
[1] TRUE

# Вектор строк

> suits <- c("spades", "hearts", "diamonds", "clubs")
> suits
[1] "spades" "hearts" "diamonds" "clubs"   

# Длина вектора получается функцией `length()`

> length(remain)
[1] 4

# Сумма элементов функцией `sum()`

> sum(remain)
[1] 42

Каждому элементу вектора можно присвоить имя. Сделать это можно несколькими способами:

# С помощью функции names()
> names(remain) <- suits
> remain
spades hearts diamonds clubs
    10     11       13     8

# При создания объекта
remain <- c("spades" = 10, "hearts" = 11, "diamonds" = 13, "clubs" = 8)

# Имена можно не заключать в кавычки
remain <- c(spades = 10, hearts = 11, diamonds = 13, clubs = 8)

На самом деле, все базовые типы R являются векторами из 1 элемента.

> day_number <- 1
> day_title <- "Monday"

> is.vector(day_number)
[1] TRUE

> is.vector(day_title)
[1] TRUE

> length(day_number)
[1] 1

> length(day_title)
[1] 1

При попытке создать вектор, содержащий элементы нескольких типов, все элементы будут приведены к наиболее общему типу.

# Все numeric будут конвертированы в character

> marks <- c(4, 5, "A", "F")
> marks
[1] "4" "5" "A" "F"

>class(manks)
[1] "character"

Вычисления с векторами

Операции над векторами выполняются поэлементно:

> earnings <- c(10, 20, 30)
> earnings * 3
[1] 30 60 90

> sqrt(earnings)
[1] 3.162278 4.472136 5.477226

# Операции над несколькими векторами так же выполняются поэлементно
> expenses <- (5, 10, 40)
> earnings - expenses
[1] 5 10 -10

> earnings * c(1, 2, 3)
[1] 10 40 90

> earnings / c(1, 2, 3)
[1] 10, 10, 10

# Каждый элемент вектора можно проверить на соответсиве условию и получить вектор logic значений
> earnings > 15
[1] FALSE TRUE TRUE

Элементы вектора, подмножества векторов

Обращение к элементам вектора происходит по индексу или имени, заключенному в квадратные скобки []. О плохом: индексация в R начинается с 1.

> remain <- c(spades = 10, hearts = 11, diamonds = 13, clubs = 8)
>

# По индексу
> remain[1]
spades
    10

# По имени
> remain["hearts"]
hearts
    11

Можно получить несколько элементов вектора, используя числовой вектор или вектор строк.

# По индесам
> remain[c(1, 4)]
spades clubs
    10     8

# По именам
> remain[c("diamonds", "hearts")]
diamonds hearts
      13     11     

Если поставить перед индексом знак минус -, этот элемент будет исключен из вектора. Удалять элементы по именам нельзя, только по индексам.

# Новый вектор, содержащий все элементы remain кроме третьего
> new_remain < remain[-3]
> new_remain
spades hearts clubs
    10     11     8

# Можно исключить сразу несколько элементов
> remain[-c(1, 3)]
hearts clubs
    11     8

Подмножество элементов можно получить передав вектор logic значений. Будут выбраны элементы индексам которых соответствует значение TRUE.

# Выбираем второй и четвертый элементы
> remain[c(FALSE, TRUE, FALSE, TRUE)]
hearts clubs
    11     8

# Если элементов в передаваемом векторе-запросе недостаточно, он будет зациклен до нужной длины
> remain[c(FALSE, TRUE)]
hearts clubs
    11     8

# Выбираем нечетные элементы
>remain[remain %% 2 != 0]
hearts diamonds
    11       13

Матрицы

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

# Матрица содержащая элементы от 1 до 6 в две строки
> matrix(1:6, nrow = 2)
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

# В два столбца
> matrix(1:6, ncol = 2)
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6

# По умолчанию матрица заполняется по столбцам, для заполнения по строкам нужен параметр "byrow"
> matrix(4:11, nrow = 2, byrow = TRUE)
     [,1] [,2] [,3] [,4]
[1,]    4    5    6    7
[2,]    8    9   10   11

# Если элементов недостаточно для заполнения всех строк и столбцов они будут циклически повторяться
> matrix(1:3, nrow = 3, ncol = 4, byrow = TRUE)
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    1
[2,]    2    3    1    2
[3,]    3    1    2    3

Второй способ создания матрицы состоит в комбинировании векторов по строкам или по столбцам, функциями rbind и cbind соответственно.

> rbind(1:3, 7:9)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    7    8    9

> cbind(4:6, 1:3)
     [,1] [,2]
[1,]    4    1
[2,]    5    2
[3,]    6    3

Используя rbind и cbind можно добавить строку или столбец к уже существующей матрице.

> m <- matrix(1:6, byrow = TRUE, nrow = 2)
> m
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6

> rbind(m, 7:9)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

> cbind(m, c(10:11))
     [,1] [,2] [,3] [,4]
[1,]    1    2    3   10
[2,]    4    5    6   11

Для присвоения имен строкам и столбцам матрицы есть функции rownames и colnames. Строки и столбцы могут быть проименованы и в момент создания матрицы параметром dimnames.

> m <- matrix(1:9, nrow = 3, ncol = 3, byrow = TRUE)
>

# Называем стоки
> rownames(m) <- c("row1", "row2", "row3")
>

# Называем столбцы
> colnames(m) <- c("col1", "col2", "col3")
> m
     col1 col2 col3
row1    1    2    3
row2    4    5    6
row3    7    8    9

# При создания матрицы
> m <- matrix(1:9, byrow = TRUE, nrow = 3, ncol = 3,
                dimnames = list(c("row1", "row2", "row3"),
                                c("col1", "col2", "col3")))

Как и векторы, матрицы хранят элементы только одного типа. При попытке сохранить в одной матрице элементы нескольких типов, или соединить матрицы разных типов используя rbind и cbind все элементы будут приведены к наиболее общему типу.

Элементы матрицы, подмножества матриц

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

# Выбираем элемент из первой строки, третьего столбца
> m[1, 3]
[1] 3

Если не указан один из индексов, будет получена целая строка или столбец.

# Выбираем вторую строку
> m[2, ]
col1 col2 col3
   4    5    6

# Выбираем третий столбец
> m[, 3]
row1 row2 row3
   3    6    9

Можно выбирать сразу несколько элементов. Принципы те же, что и с векторами.

# Элементы на пересечении второй строки, первого и третьего столбца
> m[2, c(1, 3)]
col1 col3
   4    6

# Подматрица, состоящая из элементов на пересечении 1, 3 строк и 1, 3 столбцов
> m[c(1, 3), c (1, 3)]
     col1 col3
row1    1    3
row3    7    9

Элементы можно выбрать используя имена строк и столбцов.

> m[c("row1", "row3"), "col1"]
row1 row3
   1    7

# Индексы и имена можно использовать вместе
> m[2, c("col2", "col3")]
col2 col3
   5    6

Использование матриц

Матрицы поддерживают все стандартные арифметические операции. Выполняются операции над матрицами поэлементно, включая умножение и деление.

# Переменная будет добавлена к каждому элементу
> m + 3
     col1 col2 col3
row1    4    5    6
row2    7    8    9
row3   10   11   12

# Функция применяется к каждом элементу
> log(m)
         col1      col2     col3
row1 0.000000 0.6931472 1.098612
row2 1.386294 1.6094379 1.791759
row3 1.945910 2.0794415 2.197225

# "rowSums" возвращает сумму элементов каждой строки
> rowSums(m)
row1 row2 row3
   6   15   24

# "colSums" возвращает сумму элементов каждого столбца
> colSums(m)
col1 col2 col3
  12   15   18

# Транспонирование матриц осуществляется функцией "t()"
> t(m)
     row1 row2 row3
col1    1    4    7
col2    2    5    8
col3    3    6    9

# Для математической операции перемножения матриц необходимо использовать оператор %*%
> matrix(1:6, nrow = 3) %*% matrix(7:12, ncol = 3)
     [,1] [,2] [,3]
[1,]   39   49   59
[2,]   54   68   82
[3,]   69   87  105