Получение информации об объекте

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

Все объекты в R обладают рядом базовых характеристик — класс объекта, который видит и использует пользователь, внутренний класс объекта, который используется в ядре R, и тип хранения объектов в памяти. Классы обычно понимаются как классы S3-/S4-/R6-систем ООП.

Для определения этих характеристик используются следующие функции:

  • class() определяет класс объекта:
# создаём вектор и смотрим его класс
x <- c('a', 'c', 'e')
class(x)
## [1] "character"
# создаём таблицу и смотрим её класс
z <- data.frame(var1 = letters[1:5])
class(z)
## [1] "data.frame"
  • typeof() определяет внутренние для R типы объектов, например, таблица имеет класс data.frame, но в ядре R это тип list и обрабатывается идентично:
## [1] "list"
  • mode() или storage.mode() возвращают один из вариантов, с каким типом хранится объект в памяти. Обычно результаты совпадают с typeof, но не всегда. Например, массивы — это те же векторы, просто с атрибутом размерности, поэтому и в памяти они хранятся с тем же типом, что и векторы:
mx <- matrix('a', nrow = 2, ncol = 2)
print(mx)
##      [,1] [,2]
## [1,] "a"  "a" 
## [2,] "a"  "a"
class(mx)
## [1] "matrix" "array"
typeof(mx)
## [1] "character"
mode(mx)
## [1] "character"
  • is.numeric(), is.character(), is.logical() и прочие функции вида is.* проверяют, является ли объект объектом определённого класса или нет:
is.character('Jane Dow')
## [1] TRUE
## [1] TRUE

Измерения и длина объектов

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

  • length() для оценки количества элементов объекта. Например, количество элементов в векторе или количество элементов в списке. Так как data.frame-таблицы также являются списками, то для таблиц length() отдаёт количество колонок:
# создаём список, в первом элементе которого вектор латинских букв, во втором — названия месяцев
x <- list(lt = letters,
          mnth = month.name)

# определяем длину первого элемента вектора
length(x$lt)
## [1] 26
# определяем количество элементов в векторе
length(x)
## [1] 2
# определяем количество колонок в таблице iris
length(iris)
## [1] 5
  • lengths() для оценки длин всех элементов списка одновременно:
##   lt mnth 
##   26   12
  • nrow() используется для оценки количества строк в таблице:
nrow(iris)
## [1] 150
  • ncol() используется аналогично length() для оценки количества колонок в таблице:
ncol(iris)
## [1] 5

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

# смотрим первые 6 строк датасета iris
head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa
dim(iris)
## [1] 150   5

Имена элементов и измерений объектов

Для получения и изменения названий элементов объектов — колонок в таблицах, элементов в именованных списках и векторах — используют функцию names(). Для массивов используется dimnames():

# создаём таблицу с колонками col1 и col2, получаем их названия
new_df <- data.frame(col1 = month.name[1:3], col2 = month.abb[1:3])
print(new_df)
##       col1 col2
## 1  January  Jan
## 2 February  Feb
## 3    March  Mar
names(new_df)
## [1] "col1" "col2"
# создаём матрицу 2*2
new_mx <- matrix(1:4, nrow = 2, ncol = 2, dimnames = list(c('dim11', 'dim12'), c('dim21', 'dim22')))
print(new_mx)
##       dim21 dim22
## dim11     1     3
## dim12     2     4
dimnames(new_mx)
## [[1]]
## [1] "dim11" "dim12"
## 
## [[2]]
## [1] "dim21" "dim22"

При работе с таблицами можно также использовать функции colnames() и rownames() для получения и изменения названий колонок и строк соответственно. Стоит учесть, что если строкам таблицы не были присвоены названия, то названия всё равно будут в виде вектора значений 1:nrow(dataframe), преобразованных в текст:

print(new_df)
##       col1 col2
## 1  January  Jan
## 2 February  Feb
## 3    March  Mar
colnames(new_df)
## [1] "col1" "col2"
rownames(new_df)
## [1] "1" "2" "3"

Имена могут быть неуникальными, однако это затрудняет некоторые операции (выделение подвыборки, объединение строк или столбцов), так что подобных ситуаций следует избегать. В некоторых случаях у элементов объектов может не быть имён, например, names(5) возвращает NULL. Для data.frame-таблиц запрещены неуникальные названия строк.

Изменение имён в базовом R идёт несколько нехарактерным для большинства объектов R способом: новые значения присваиваются не какому-то объекту, а выражению вида names(x). Это возможно, так как для подобных функций определён отдельный метод names<-, который обновляет вызываемый объект:

# меняем названия колонок на col1, col2 и выводим таблицу
rownames(new_df) <- c('raw1', 'raw2', 'raw3')
print(new_df)
##          col1 col2
## raw1  January  Jan
## raw2 February  Feb
## raw3    March  Mar

Атрибуты объектов

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

Один из самых распространённых случаев использования атрибутов хорошо знаком всем, кто импортирует SPSS-файлы в R. Так как в SPSS-файлах хранятся как сами значения, так и их метки (например, что 99 означает пропуск данных, а 999 — отказ от ответа), то при импорте в R значения образуют таблицу, а метки оказываются в атрибутах колонок таблицы. Подробнее в Чтение файлов SPSS.

Список атрибутов и их значений можно получить с помощью функции attributes() либо вызвать значения конкретного атрибута с помощью функции attr():

# запрашиваем список атрибутов
attributes(mtcars)
## $names
##  [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear"
## [11] "carb"
## 
## $row.names
##  [1] "Mazda RX4"           "Mazda RX4 Wag"       "Datsun 710"         
##  [4] "Hornet 4 Drive"      "Hornet Sportabout"   "Valiant"            
##  [7] "Duster 360"          "Merc 240D"           "Merc 230"           
## [10] "Merc 280"            "Merc 280C"           "Merc 450SE"         
## [13] "Merc 450SL"          "Merc 450SLC"         "Cadillac Fleetwood" 
## [16] "Lincoln Continental" "Chrysler Imperial"   "Fiat 128"           
## [19] "Honda Civic"         "Toyota Corolla"      "Toyota Corona"      
## [22] "Dodge Challenger"    "AMC Javelin"         "Camaro Z28"         
## [25] "Pontiac Firebird"    "Fiat X1-9"           "Porsche 914-2"      
## [28] "Lotus Europa"        "Ford Pantera L"      "Ferrari Dino"       
## [31] "Maserati Bora"       "Volvo 142E"         
## 
## $class
## [1] "data.frame"
# запрашиваем, какие имена колонок объекта
attr(mtcars, 'names')
##  [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear"
## [11] "carb"

Если у объекта нет атрибутов, то будет возвращен NULL (как правило, большинство функций R при создании объекта всё же создают и его атрибуты):

# создаём объект и не создаём для него атрибуты
x <- 5
attributes(x)
## NULL
# используем базовую функцию для создания таблицы
x <- data.frame(v1 = 1:5)
attributes(x)
## $names
## [1] "v1"
## 
## $class
## [1] "data.frame"
## 
## $row.names
## [1] 1 2 3 4 5

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

# создаём простой объект
x <- 'new object'

# создаём атрибут "description", в который записываем описание объекта
attr(x, 'description') <- 'simple text string'

# создаём дополнительный атрибут, в который записываем отдельную таблицу
attr(x, 'additional_table') <- data.frame(var1 = 1:5, var2 = letters[1:5])

# смотрим атрибуты объекта
attributes(x)
## $description
## [1] "simple text string"
## 
## $additional_table
##   var1 var2
## 1    1    a
## 2    2    b
## 3    3    c
## 4    4    d
## 5    5    e

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

add_label_df <- attr(x, 'additional_table')
str(add_label_df)
## 'data.frame':    5 obs. of  2 variables:
##  $ var1: int  1 2 3 4 5
##  $ var2: chr  "a" "b" "c" "d" ...

Структура объектов

Нередко при работе с разными объектами необходимо получить сводную информацию об объекте — класс объекта, иерархию его элементов, первые значения каждого элемента и т. д. Для этих целей используется функция str() (от structure), которая выводит каждый элемент объекта в виде вектора значений. Например, при просмотре структуры таблицы iris мы получаем класс объекта (data.frame), количество строк и столбцов, название колонок, тип элементов и первые десять значений каждой колонки:

str(iris)
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

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

x <- list(e1 = 1:5,
          e2 = letters[1:5],
          e3 = list(e31 = rnorm(5), 
                    e32 = runif(5)))
str(x)
## List of 3
##  $ e1: int [1:5] 1 2 3 4 5
##  $ e2: chr [1:5] "a" "b" "c" "d" ...
##  $ e3:List of 2
##   ..$ e31: num [1:5] 0.488 -0.629 0.559 0.687 -0.435
##   ..$ e32: num [1:5] 0.817 0.961 0.785 0.48 0.825