Всеобъемлющий обзор типов вещей в R. "mode", "class" и "typeof" недостаточен

язык 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"?
  • какая другая информация необходима для полного описания сущности? (Где 'vectorness' хранится, пример?)

обновление: по-видимому, литерал 3 - это просто вектор длины 1. Скаляров нет. ОК но... Я пытался!--2--> и получил "character", заставляя меня думать, что строка была вектором символов. Но если это правда, то это должно быть правдой, но это не так! c('h','i') == "hi"

4 ответов


я согласен, что система типов в 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 returns). Но когда атрибут class не установлен,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"

вот некоторый код, чтобы определить, что четыре типа функций,класс, режим, typeof на и хранения.режим вернуться для каждого из видов объекта 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,
  "builtin 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     |
|builtin 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 Определение Языка руководство. Есть несколько типов, не упомянутых здесь: вы не можете тестировать объекты типа "promise", "...", и "Любой", "байт-код" и "weakref" доступны только на уровне C.

таблица доступных типов в источнике R -здесь.


добавление к одному из ваших под-вопросов:

  • какая другая информация необходима для полного описания сущности?

кроме 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 строк.

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

редактировать

чтобы включить еще больше функций "is", согласно комментарию @Erdogan, вы можете добавить этот бит в функцию:

  # right after 
  # list.fun <- grep(methods(is), pattern = "<-", invert = TRUE, value = TRUE)
  list.fun.2 <- character()

  packs <- c('base', 'utils', 'methods') # include more packages if needed

  for (pkg in packs) {
    library(pkg, character.only = TRUE)
    objects <- grep("^is.+\w$", ls(envir = as.environment(paste('package', pkg, sep = ':'))),
                    value = TRUE)
    objects <- grep("<-", objects, invert = TRUE, value = TRUE)
    if (length(objects) > 0) 
      list.fun.2 <- append(list.fun.2, objects[sapply(objects, function(x) class(eval(parse(text = x))) == "function")])
  }

  list.fun <- union(list.fun.1, list.fun.2)  

  # ...and continue with the rest
  result <- data.frame(test=character(), value=character(), 
                       warning=character(), stringsAsFactors = FALSE)
  # and so on...

все ли в R имеет (точно один) класс ?

точно один определенно не прав:

> x <- 3
> class(x) <- c("hi","low")
> class(x)
[1] "hi"  "low"

все имеет (по крайней мере, один) класс.

все ли в R имеет (точно один) режим ?

не уверен, но подозреваю, что да.

что, если что-нибудь, говорит нам "typeof"?

typeof дает внутренний тип объекта. Возможное значение согласно ?typeof являются:

векторные типы "логические", "целочисленные", "двойные", " сложные", "символ", " raw " и "list", "NULL", "closure" (функция), " special" и " builtin "(основные функции и операторы), "окружающая среда", " S4" (некоторые объекты S4) и другие, которые вряд ли будут видны пользователю уровень ("символ", "pairlist", "обещание", "язык", "char","...", "любой", "выражение", "externalptr"," байт-код " и "weakref").

mode полагается на оператора typeof. От ?mode:

режимы имеют тот же набор имен, что и типы (см. typeof), за исключением типы "integer" и "double"возвращаются как "numeric". типы "special" и "builtin"возвращаются как "функция". тип "символ" называется режимом "имя". тип "language" возвращается как " ("или " call".

какая другая информация необходима для полного описания сущности? (Где в 'listness' хранить, например?)

список имеет список классов:

> y <- list(3)
> class(y)
[1] "list"

вы имеете в виду векторизацию? length должно быть достаточно для большинства целей:

> z <- 3
> class(z)
[1] "numeric"
> length(z)
[1] 1

думать 3 как числовой вектор длины 1, а не как некоторый примитивный числовой тип.

вывод

вы можете получить, просто отлично с class и length. К тому времени, когда вам понадобятся другие вещи, у вас, вероятно, не будет спросить, для чего они нужны :-)