Всестороннее исследование типов вещей в "режиме" и "классе" и "типе" недостаточно
Язык R меня смущает. У объектов есть режимы и классы, но даже этого недостаточно, чтобы полностью описать сущность.
Этот ответ говорит
В R каждый "объект" имеет режим и класс.
Итак, я сделал эти эксперименты:
> class(3)
[1] "numeric"
> mode(3)
[1] "numeric"
> typeof(3)
[1] "double"
Достаточно справедливо, но затем я перешел в вектор:
> mode(c(1,2))
[1] "numeric"
> class(c(1,2))
[1] "numeric"
> typeof(c(1,2))
[1] "double"
Это не имеет смысла. Конечно, вектор целых чисел должен иметь другой класс или другой режим, чем одно целое? Мои вопросы:
- Все ли в R есть (только один) класс?
- Все ли в R есть (только один) режим?
- Что, если что-нибудь, говорит нам "typeof"?
- Какая еще информация необходима для полного описания объекта? (Где хранится "векторность", например?)
Обновить. По-видимому, буква 3 является всего лишь вектором длины 1. Нет скаляров. OK Но... Я пробовал mode("string")
и получил "character"
, что заставило меня думать, что строка является вектором символов. Но если это правда, тогда это должно быть правдой, но это не так! c('h','i') == "hi"
Ответы
Ответ 1
Я согласен, что система типов в R довольно странная. Причиной этого является то, что он эволюционировал в течение (длительного) времени...
Обратите внимание, что вы пропустили еще одну подобную типу функцию, storage.mode
и еще одну класс-функцию, oldClass
.
Итак, mode
и storage.mode
- это типы старого стиля (где storage.mode
более точная), а typeof
- более новая, еще более точная версия.
mode(3L) # numeric
storage.mode(3L) # integer
storage.mode(`identical`) # function
storage.mode(`if`) # function
typeof(`identical`) # closure
typeof(`if`) # special
Тогда class
- совершенно другая история. class
- это в основном только атрибут class
объекта (что именно возвращает oldClass
). Но когда атрибут класса не задан, функция class
представляет класс из типа объекта и атрибута dim.
oldClass(3L) # NULL
class(3L) # integer
class(structure(3L, dim=1)) # array
class(structure(3L, dim=c(1,1))) # matrix
class(list()) # list
class(structure(list(1), dim=1)) # array
class(structure(list(1), dim=c(1,1))) # matrix
class(structure(list(1), dim=1, class='foo')) # foo
Наконец, класс может возвращать несколько строк, но только в том случае, если атрибут class подобен этому. Первое строковое значение представляет собой вид основного класса, а следующие - то, на что он наследует. Выделенные классы всегда имеют длину 1.
# Here "A" inherits from "B", which inherits from "C"
class(structure(1, class=LETTERS[1:3])) # "A" "B" "C"
# an ordered factor:
class(ordered(3:1)) # "ordered" "factor"
Ответ 2
Все ли в R есть (только один) класс?
Точно одно однозначно неверно:
> x <- 3
> class(x) <- c("hi","low")
> class(x)
[1] "hi" "low"
У всех есть (по крайней мере один) класс.
Все ли в R есть (только один) режим?
Не уверен, но я подозреваю, что так.
Что, если что-нибудь, говорит нам "typeof"?
typeof
дает внутренний тип объекта. Возможные значения в соответствии с ?typeof
:
Типы векторов "логический", "целочисленный", "двойной", "сложный", "character", "raw" и "list", "NULL", "замыкание" (функция), "специальный", и "встроенные" (базовые функции и операторы), "среда", "S4", (некоторые объекты S4) и другие, которые вряд ли будут видны у пользователя ( "символ", "парный список", "обещание", "язык", "char", "...", "any", "expression", "externalptr", "bytecode" и "weakref" ).
mode
полагается на typeof. От ?mode
:
Режимы имеют одинаковый набор имен как типы (см. typeof), за исключением того, что типы "integer" и "double" возвращаются как "числовые". типы "специальные" и "встроенные" возвращаются как "функция". Тип "символ" называется режимом "имя". type "language" возвращается как "(" или "call".
Какая еще информация необходима для полного описания объекта? (Где хранится "список", например?)
Список имеет список классов:
> y <- list(3)
> class(y)
[1] "list"
Вы имеете в виду векторизацию? length
должно быть достаточным для большинства целей:
> z <- 3
> class(z)
[1] "numeric"
> length(z)
[1] 1
Вспомните 3
как числовой вектор длины 1, а не как некоторый примитивный числовой тип.
Заключение
Вы можете отлично справиться с class
и length
. К тому времени, когда вам понадобятся другие вещи, вам, вероятно, не придется спрашивать, для чего они предназначены: -)
Ответ 3
Здесь некоторый код для определения функций четырех типов, class, mode, typeof и storage.mode возвращает для каждого из видов объекта R.
library(methods)
library(dplyr)
library(xml2)
setClass("dummy", representation(x="numeric", y="numeric"))
types <- list(
"logical vector" = logical(),
"integer vector" = integer(),
"numeric vector" = numeric(),
"complex vector" = complex(),
"character vector" = character(),
"raw vector" = raw(),
factor = factor(),
"logical matrix" = matrix(logical()),
"numeric matrix" = matrix(numeric()),
"logical array" = array(logical(8), c(2, 2, 2)),
"numeric array" = array(numeric(8), c(2, 2, 2)),
list = list(),
pairlist = .Options,
"data frame" = data.frame(),
"closure function" = identity,
"primitive function" = `+`,
"special function" = `if`,
environment = new.env(),
null = NULL,
formula = y ~ x,
expression = expression(),
call = call("identity"),
name = as.name("x"),
"paren in expression" = expression((1))[[1]],
"brace in expression" = expression({1})[[1]],
"S3 lm object" = lm(dist ~ speed, cars),
"S4 dummy object" = new("dummy", x = 1:10, y = rnorm(10)),
"external pointer" = read_xml("<foo><bar /></foo>")$node
)
type_info <- Map(
function(x, nm)
{
data_frame(
"spoken type" = nm,
class = class(x),
mode = mode(x),
typeof = typeof(x),
storage.mode = storage.mode(x)
)
},
types,
names(types)
) %>% bind_rows
knitr::kable(type_info)
Здесь вывод:
|spoken type |class |mode |typeof |storage.mode |
|:-------------------|:-----------|:-----------|:-----------|:------------|
|logical vector |logical |logical |logical |logical |
|integer vector |integer |numeric |integer |integer |
|numeric vector |numeric |numeric |double |double |
|complex vector |complex |complex |complex |complex |
|character vector |character |character |character |character |
|raw vector |raw |raw |raw |raw |
|factor |factor |numeric |integer |integer |
|logical matrix |matrix |logical |logical |logical |
|numeric matrix |matrix |numeric |double |double |
|logical array |array |logical |logical |logical |
|numeric array |array |numeric |double |double |
|list |list |list |list |list |
|pairlist |pairlist |pairlist |pairlist |pairlist |
|data frame |data.frame |list |list |list |
|closure function |function |function |closure |function |
|primitive function |function |function |builtin |function |
|special function |function |function |special |function |
|environment |environment |environment |environment |environment |
|null |NULL |NULL |NULL |NULL |
|formula |formula |call |language |language |
|expression |expression |expression |expression |expression |
|call |call |call |language |language |
|name |name |name |symbol |symbol |
|paren in expression |( |( |language |language |
|brace in expression |{ |call |language |language |
|S3 lm object |lm |list |list |list |
|S4 dummy object |dummy |S4 |S4 |S4 |
|external pointer |externalptr |externalptr |externalptr |externalptr |
Типы объектов, доступных в R, обсуждаются в руководстве R Language Definition. Существует несколько типов, не упомянутых здесь: вы не можете тестировать объекты типа "обещание", "..." и "ЛЮБОЙ", а "байт-код" и "слабый" доступны только на уровне C.
Таблица доступных типов в источнике R здесь.
Ответ 4
Добавление к одному из ваших вопросов:
- Какая еще информация необходима для полного описания объекта?
В дополнение к class
, mode
, typeof
, attributes
, str
и т.д. также стоит отметить is()
.
is(1)
[1] "numeric" "vector"
Хотя это полезно, оно также неудовлетворительно. В этом примере 1
больше, чем просто; он также является атомарным, конечным и двойным. Следующая функция должна показывать все, что объект соответствует всем доступным функциям is.(...)
:
what.is <- function(x, show.all=FALSE) {
# set the warn option to -1 to temporarily ignore warnings
op <- options("warn")
options(warn = -1)
on.exit(options(op))
list.fun <- grep(methods(is), pattern = "<-", invert = TRUE, value = TRUE)
result <- data.frame(test=character(), value=character(),
warning=character(), stringsAsFactors = FALSE)
# loop over all "is.(...)" functions and store the results
for(fun in list.fun) {
res <- try(eval(call(fun,x)),silent=TRUE)
if(class(res)=="try-error") {
next() # ignore tests that yield an error
} else if (length(res)>1) {
warn <- "*Applies only to the first element of the provided object"
value <- paste(res,"*",sep="")
} else {
warn <- ""
value <- res
}
result[nrow(result)+1,] <- list(fun, value, warn)
}
# sort the results
result <- result[order(result$value,decreasing = TRUE),]
rownames(result) <- NULL
if(show.all)
return(result)
else
return(result[which(result$value=="TRUE"),])
}
Итак, теперь мы получаем более полную картину:
> what.is(1)
test value warning
1 is.atomic TRUE
2 is.double TRUE
3 is.finite TRUE
4 is.numeric TRUE
5 is.vector TRUE
> what.is(CO2)
test value warning
1 is.data.frame TRUE
2 is.list TRUE
3 is.object TRUE
4 is.recursive TRUE
Вы также получите дополнительную информацию с аргументом show.all=TRUE
. Я не вставляю здесь какой-либо пример, поскольку результаты имеют длину более 50 строк.
Наконец, это подразумевается как дополнительный источник информации, а не как замена любой из других функций, упомянутых ранее.