Объекты и типы данных языка R

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

  1. Типы данных.
  2. Объекты языка R.
  3. Классы объектов.

Каждый язык программирования предполагает наличие одного или нескольких типов данных. Тип данных характеризует некоторое множество значений и операции, которые можно применять к этим значениям. Любые данные, используемые в программе, относятся к тому или иному типу. Так, например, строковый тип предполагает, что объект содержит произвольное количество любых символов. Операции применимые к строкам – это объединение, разбиение, замена символов, но никак не арифметические вычисления.

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

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

  • numeric – числовой тип, который включает в себя как целые числа, так и дроби;
  • integer – целочисленный тип;
  • character – символьный тип данных: каждый элемент в таком векторе является последовательностью из одного или более символов. Следует отметить, что совокупность элементов символьного вектора не является единой строкой;
  • complex – комплексный тип;
  • logical – логический тип, принимает значения TRUE или FALSE.

Numeric

Объекты этого типа могут содержать только числа. Эквивалентные обозначения: double или real. Последнее существует только для сохранения обратной совместимости со старым кодом. Таким образом, в языке R имеют место три варианта обозначения векторов, содержащих числа с плавающей точкой. Очевидно, что объекты данного типа созданы для выполнения математических операций. Проверка на принадлежность переменной типу numeric выполняется функцией is.numeric().

Integer

Этот тип создан для того, чтобы обеспечивать совместимость с кодом на языках C или Fortran таким образом, чтобы представить целочисленные данные наиболее компактно. Следует отметить, что в актуальной на сегодня реализации R используется 32-битный тип integer, разброс значений которого ограничен +/- 2*10^9. В свою очередь, объекты типа double могут вмещать целые числа из более широкого диапазона. Чтобы убедиться, что некоторый вектор содержит целые числа можно использовать функцию is.inetger().

Character

Тип данных character создан для выполнения операций с символами. Символом может быть что угодно: буквы алфавита, доступные в той или иной кодировке, цифры, а так же любой другой символ, который пользователь сможет найти на своей клавиатуре. Понятно, что со строковыми векторами невозможно проводить никаких вычислений, даже если в них содержатся цифры. В языке R есть предопределенные константы 'letters' и 'LETTERS' которые содержат соответственно строчные и прописные буквы английского алфавита.

В тех случаях, когда с цифрами, содержащимися в строковых векторах, необходимо провести вычисления, их следует преобразовать в числовой тип функциями as.integer() или as.numeric(). Соответственно, если мы хотим преобразовать число в строку, то используется функция as.character(), а для выяснения типа переменной - is.character().

Complex

Комплексные векторы содержат комплексные числа. Больше об этом типе данных мне сказать нечего.

Logical

Логические переменные могут принимать только два значения TRUE (истина) и FALSE (ложь). TRUE и FALSE – зарезервированные слова языка R, обозначающие логические константы. Эти обозначения в программном коде можно заменять символами 'T' и 'F'. Если попытаться конвертировать логический вектор в вектор типа integer, то в результате все значения TRUE будут заменены на 1, а FALSE – на 0.

Объекты языка R

Вектор является объектом языка R и, как уже было сказано, может содержать компоненты только одного типа, а так же NA-значения, т. е. пропущенные значения, которые могут быть в векторе любого типа. Однако в R есть и другие объекты: матрицы, списки, таблицы, функции, факторы. Тем не менее, в основе всех этих типов объектов лежит базовый объект – вектор.

Каждый объект имеет ряд атрибутов, характеризующих его свойства. Основные атрибуты – это тип объекта и его длина. Под типом объекта понимается тот тип данных, к которому принадлежат его компоненты. Например, вектор, содержащий числовые значения типа numeric, является объектом типа numeric. В свою очередь длина объекта – это количество элементов, которые он содержит. Для определения типа и длины объектов используются функции mode() и length() соответственно. Функция length() позволяет еще и задавать размер объекта: так строка length(x) <- 3 задает вектору x размер в три единицы. Если ранее в векторе x было более трех компонентов, то данное действие удалит все остальные значения. Если же длина вектора была менее трех, то функция добавит недостающее количество NA-значений. Следует отметить, что даже пустой вектор принадлежит определенному типу – так пустой символьный вектор обозначается character(0).

Оба описанных атрибута называют внутренними свойствами объекта. Для того, чтобы получить все остальные, если они есть, необходимо использовать функцию attributes(). В том случае, когда мы хотим получить определенные атрибуты объекта, не являющиеся внутренними, используется функция attr().

Классы объектов

Один из атрибутов любого объекта – это класс. Принадлежность к тому или иному классу определяет способ представления данных пользователю, а также их обработку некоторыми функциями. Данный атрибут является символьным вектором, содержащим список классов, к которым относится данный объект. Пользователь может изменять значения этого вектора без каких-либо ограничений, при этом проверка на соответствие содержания этого атрибута и фактическую принадлежность объекта некоторому классу не производится. Другими словами, можно задать любое, даже ничего не значащее, строковое значение атрибуту class через функцию class(). Следовательно, пользоваться этой возможностью следует крайне осторожно.

Матрицы

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

Массив, в отличие от вектора, имеет атрибут dim, содержащий целочисленные значения, задающие количество элементов для каждого измерения массива. Так, у матрицы 10*5 атрибут dim задается вектором «10, 5». Отсюда вытекает еще один способ формирования массивов - это задание атрибута dim некоторому вектору. Для примера создадим вектор x из 50 элементов, а затем превратим его в двумерный массив (матрицу):

> x <- 1:50
> attr(x, "dim") <- c(5, 10)
> attr(x, "dim")
[1]  5 10
> x
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    1    6   11   16   21   26   31   36   41    46
[2,]    2    7   12   17   22   27   32   37   42    47
[3,]    3    8   13   18   23   28   33   38   43    48
[4,]    4    9   14   19   24   29   34   39   44    49
[5,]    5   10   15   20   25   30   35   40   45    50
> class(x)
[1] "matrix"


Возможна другая запись:

> dim(x)
<- c(5, 10)


Следует сказать, что вновь сделать из матрицы вектор можно, присвоив атрибуту dim значение NULL:

> dim(x) <- NULL
> is.vector(x)
[1] TRUE
> class(x)
[1] "integer"
> x
[1]   1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
[26] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50


Если же сделать массив одномерным, то фактически он тоже станет вектором, но формально будет принадлежать классу array. Как правило, такие массивы обрабатываются как векторы, но в некоторых случаях могут быть и исключения:

> dim(x) <- length(x)
> is.vector(x)
[1] FALSE
> class(x)
[1] "array"
> x
[1]   1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
[26] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50


Для извлечения из массива определенного элемента используется такой же принцип индексации, как и в векторах, только с учетом размерности массива. Например, чтобы извлечь из матрицы 5*10 значение, находящееся в пятом ряду и третьем столбце, следует написать x[5, 3].

Факторы

Фактор – это специальный тип объектов, предназначенный для работы с категориальными или порядковыми данными. Категориальные данные, или данные, измеренные в номинальной шкале, не имеют количественного выражения. Примером таких значений могут быть названия городов или имена людей. Порядковые переменные отличаются от категориальных тем, что позволяют упорядочивать объекты. Типичный пример порядковой или ординальной переменной – уровень доходов человека: низкий, средний, высокий. Очевидно, что низкий ниже среднего, а средний ниже высокого. Однако величину различий порядковая шкала измерений, как и номинальная, установить не позволяет.

Объект класса factor можно получить из числового или строкового вектора с помощью функции factor(). Следует иметь в виду, что числовые векторы данная функция преобразует в строку, поэтому выполнение каких-либо арифметических действий с факторами, полученными из чисел, невозможно, что соответствует требованиям к данным, выраженным в качественной шкале измерений.

Может показаться, что факторы – это излишество, поскольку в R уже есть тип строковых данных character. Тем не менее, это не так. Объекты данного класса позволяют эффективно использовать категориальные и порядковые переменные. В отличие от строковых векторов, эти объекты имеют два аттрибута: levels и class. Аттрибут class содержит название класса factor, а levels – уровни фактора. Уровни фактора – это все уникальные значения переменной.

Рассмотрим пример работы с факторами. Для начала создадим строковый вектор, содержащий обозначения двух цветов – пять элементов "white" и шесть элементов "black":

> color <- rep(c("white", "black"), c(5, 6))
color
[1] "white" "white" "white" "white" "white" "black" "black" "black" "black"
[10] "black" "black"
> is.character(color)
[1] TRUE
> is.vector(color)
[1] TRUE
> is.factor(color)
[1] FALSE
> attributes(color)
NULL


Функция is.factor() показала, что color не является фактором. Это обычный символьный вектор без атрибутов. После преобразования переменной получим объект color.f класса factor:

> color.f <- factor(color)
> color.f
[1] white white white white white black black black black black black
Levels: black white
> is.factor(color.f)
[1] TRUE


При выводе на экран переменной color.f мы получаем список уровней данного фактора. Для того, чтобы узнать сколько раз встречается каждый уровень используется summary():

> summary(color.f)
black white
6     5


Еще один способ получить информацию по уровням&nbsp;&ndash; использование функции levels():

> levels(color.f)
[1] "black" "white"


В некоторых случаях факторы необходимо упорядочивать. Например, градация уровней доходов населения предусматривает определенную последовательность: от низких доходов к высоким. По умолчанию уровни фактора сортируются в алфавитном порядке. Рассмотрим пример:

> earn <- c("low", "middle", "low", "low", "low", "low", "middle", "low", "middle", "middle", "middle", "middle", "middle", "high", "high", "low", "middle", "middle", "low", "high")
> earn.f.bad.order <- factor(earn)
> levels(earn.f.bad.order)
[1] "high"   "low"    "middle"


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

> earn.f <- factor(earn, levels = c("low", "middle", "high"))
> levels(earn.f)
[1] "low"    "middle" "high"


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

> earn.f[1]<earn.f[2]
[1] NA
Предупреждение
In Ops.factor(earn.f[1], earn.f[2]) : < не значимо для факторов


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

> earn.f.ordered <- factor(earn, levels = c("low", "middle", "high"), ordered=TRUE)
> earn.f.ordered
[1]  low    middle low    low    low    low    middle low    middle middle
[11] middle middle middle high   high   low    middle middle low    high
Levels: low < middle < high
> earn.f.ordered[1]<earn.f.ordered[2]
[1] TRUE


Следует сказать, что добавление к фактору нового элемента, не входящего в подмножества его уровней, невозможно. Попробуем добавить значение very.high в переменную earn.f:

> length(earn.f)
[1] 20
> earn.f[21] <- "very.high"
Предупреждение
In `[<-.factor`(`*tmp*`, 21, value = "very.high") :
invalid factor level, NAs generated
> earn.f
[1]  low    middle low    low    low    low    middle low    middle middle
[11] middle middle middle high   high   low    middle middle low    high
[21] <NA> 
Levels: low middle high


Как видите, вместо very.high мы получили NA. Дело в том, что в переменную-фактор можно добавить только такой элемент, который относится к одному из имеющихся уровней. Следовательно, для добавления элемента very.high необходимо создать уровень фактора с таким названием:

> earn.f <- factor(earn.f, levels=c(levels(earn.f), "very.high"))
> earn.f[21] <- "very.high"
> earn.f
[1]  low       middle    low       low       low       low       middle
[8]  low       middle    middle    middle    middle    middle    high
[15] high      low       middle    middle    low       high      very.high
Levels: low middle high very.high


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

> earn.f.trunc <- earn.f[1:5]
> earn.f.trunc
[1] low    middle low    low    low 
Levels: low middle high very.high


Удаление лишних уровней происходит при использовании специального аргумента:

> earn.f.trunc <- earn.f[1:5, drop=TRUE]
> earn.f.trunc
[1] low    middle low    low    low
Levels: low middle


Списки

Список (list) – это класс, позволяющий хранить в одной переменной объекты одного или разных типов, в том числе и другие списки. Объекты, входящие в список, могут быть не только разных типов, но и разного размера.

Создадим переменную-список:

> lst <- list(colors=c("red", "green", "blue"), hours=1:24, c(TRUE, FALSE, FALSE))
> lst
$colors
[1] "red"   "green" "blue"
$hours
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24[[3]]
[1]  TRUE FALSE FALSE


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

> class(lst)
[1] "list"
> class(lst[1])
[1] "list"
> lst[1]
$colors
[1] "red"   "green" "blue"


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

> lst[[1]]
[1] "red"   "green" "blue"
> class(lst[[1]])
[1] "character"


Можно сказать, что с помощью двойных скобок мы выделяем из списка его элемент в «чистом виде». В представленном примере это символьный вектор.

Как уже было сказано, список позволяет хранить в себе разнородные данные. А для того, чтобы работать с ними было проще, каждому элементу списка можно задать имя (имена элементов списка не могут повторяться). Такой способ позволяет лучше ориентироваться в списке, особенно если он состоит из большого числа компонентов:

> names(lst)
[1] "colors" "hours"  ""


В списке lst содержатся три элемента. Первым двум присвоены имена colors и hours, а последний не имеет имени, поэтому обращаться к нему можно только используя его порядковый номер в списке. Для вызова элементов списка по именам используются квадратные скобки или знак доллара ($). Следует отметить, что при использовании «[[]]» и «$» будут получены идентичные объекты одного класса:

> lst["colors"]
$colors
[1] "red"   "green" "blue"
> class(lst["colors"])
[1] "list"
> lst[["colors"]]
[1] "red"   "green" "blue"
> class(lst[["colors"]])
[1] "character"
> lst$colors
[1] "red"   "green" "blue"
> class(lst$colors)
[1] "character"


Интересно, что имена элементов списка можно сокращать. Единственное требование к минимальному числу символов в сокращении – это возможность точной идентификации искомого элемента списка. В нашем примере использование одной только первой буквы позволит отличить один элемент списка от другого:

> lst$c
[1] "red"   "green" "blue"
> lst$h
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24


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

> lst[4] <- list(LETTERS)
> lst
$colors
[1] "red"   "green" "blue"
$hours
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
[[3]]
[1]  TRUE FALSE FALSE
[[4]]
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
[20] "T" "U" "V" "W" "X" "Y" "Z"


Списки можно объединять так же, как и векторы. Для примера создадим два новых списка и объединим их:

> lst1 <- list(name=c("Fry", "Farnsworth"), age = c(35, 160))
> lst1
$name
[1] "Fry" "Farnsworth"
$age
[1] 35 160
> lst2 <- list(job = c("delivery.boy", "scientist"), born = c(1975, 2841))
> lst2
$job
[1] "delivery.boy" "scientist"
$born
[1] 1975 2841
> LST <- c(lst1, lst2)
> LST
$name
[1] "Fry" "Farnsworth"
$age
[1] 35 160
$job
[1] "delivery.boy" "scientist"
$born
[1] 1975 2841


Таблицы данных

Таблица данных (data.frame) – класс, позволяющий представлять данные в табличном виде. Таблица представляет собой набор векторов одинаковой длины, где каждый вектор – это столбец таблицы. Столбцы могут содержать данные разных типов, но в пределах одного столбца допустимы только однотипные значения. По сути, таблица данных – это модифицированный список, в котором каждый элемент представлен в виде столбца таблицы, и все элементы одинаковой длины. Рассмотрим созданную выше переменную LST:

> class(LST)
[1] "list"
> TBL <- as.data.frame(LST)
> class(TBL)
[1] "data.frame"
> TBL
        name age          job born
1        Fry  35 delivery.boy 1975
2 Farnsworth 160    scientist 2841


Посмотрим, какими атрибутами обладает объект класса data.frame:

> attributes(TBL)
$names
[1] "name" "age" "job" "born"
$row.names
[1] 1 2
$class
[1] "data.frame"


Как видите, кроме названия класса, у объекта имеются имена столбцов и строк. Имена строк могут быть выражены не только порядковыми номерами, но и произвольными строками. А имена столбцов являются названиями элементов списка.

Применим функцию unclass(), которая отключает все эффекты класса. В результате получим объект-список:

> uTBL <- unclass(TBL)
> uTBL
$name
[1] Fry Farnsworth
Levels: Farnsworth Fry
$age
[1] 35 160
$job
[1] delivery.boy scientist 
Levels: delivery.boy scientist
$born
[1] 1975 2841
attr(,"row.names")
[1] 1 2
> class(uTBL)
[1] "list"


Обратите внимание, что элементы списка, содержащие строковые данные, при формировании таблицы были преобразованы в факторы:

> TBL$job
[1] delivery.boy scientist 
Levels: delivery.boy scientist
> class(TBL$job)
[1] "factor"
> class(uTBL$job)
[1] "factor"


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

> TBL[1, 2]
[1] 35


Для удаления определенного столбца или строки можно использовать отрицательную индексацию. Для примера уберем первую строку:

> TBL[-1, ]
        name age       job born
2 Farnsworth 160 scientist 2841


Для присоединения к таблице нового столбца или строки используются функции cbind() и rbind() соответственно:

> cbind(TBL, sex=c("male", "male"))
        name age          job born  sex
1        Fry  35 delivery.boy 1975 male
2 Farnsworth 160    scientist 2841 male


Иногда приходится иметь дело с большими объемами данных. Выводить всю таблицу на экран для того, чтобы ознакомиться с ее структурой, в этом случае неудобно. Проблема решается функцией head(), которая выводит на экран только заголовки и первые строки таблицы.

Возмем переменную airquality, содержащую информацию о состоянии атмосферы в Нью-Йорке с мая по сентябрь 1973 года. Данная таблица входит в состав стандртной установки системы R:

> head(airquality)
  Ozone Solar.R Wind Temp Month Day
1    41     190  7.4   67     5   1
2    36     118  8.0   72     5   2
3    12     149 12.6   74     5   3
4    18     313 11.5   62     5   4
5    NA      NA 14.3   56     5   5
6    28      NA 14.9   66     5   6


Существует похожая функция tail(), которая выводит несколько последних строк таблицы:

> tail(airquality)
    Ozone Solar.R Wind Temp Month Day
148    14      20 16.6   63     9  25
149    30     193  6.9   70     9  26
150    NA     145 13.2   77     9  27
151    14     191 14.3   75     9  28
152    18     131  8.0   76     9  29
153    20     223 11.5   68     9  30


Функции

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

Создадим простейшую функцию для перевода температуры из шкалы Цельсия в шкалу Фаренгейта:

> Farenheit <- function(celsus) {
+ F <- celsus*9/5+32
+ F
+ }
> Farenheit(0)
[1] 32
> class(Farenheit)
[1] "function"


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

> Farenheit
function(celsus) {
F <- celsus*9/5+32
F
}


На этом экскурс в мир объектов и типов данных в среде R, завершен. Надеюсь этот материал окажется вам полезным.

27.02.2019 / 125 / Загрузок: 0 / Ришат Габидуллин / | Теги: язык R, статистика
Всего комментариев: 0
avatar
SixSigmaOnline.ru © 2009-2019            Хостинг от uWeb