Комплексный обзор типов вещей в R; 'mode' и 'class' и 'typeof' недостаточны - PullRequest
42 голосов
/ 13 января 2012

Язык 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 все (ровно один) class ?
  • Есть ли в R все (ровно один) mode ?
  • Что, если что-нибудь говорит нам typeof?
  • Какая другая информация необходима для полного описания сущности?(Где, например, хранится «векторность»?)

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

Ответы [ 4 ]

52 голосов
/ 14 января 2012

Я согласен, что система типов в 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 не установлен, функция 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

Наконец, класс может возвращать более одной строки, но только еслиатрибут класса такой.Тогда первое строковое значение - это вид основного класса, а следующие наследуют его.Готовые классы всегда имеют длину 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"
17 голосов
/ 21 октября 2016

Вот некоторый код для определения того, что возвращают четыре функции типа: 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,
  "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 здесь .

13 голосов
/ 18 октября 2014

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

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

В дополнение к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", согласно комментарию @ Эрдогана, вы можете добавить этот бит в функцию:

  # 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...
12 голосов
/ 13 января 2012

Есть ли у всего в R (ровно один) класс?

Точно один не прав:

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

У всего есть (хотя бы один) класс.

Есть ли у всего в R (ровно один) режим?

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

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

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

Типы векторов "логический", "целое число", "двойной", "сложный", «персонаж», «сырой» и «список», «NULL», «закрытие» (функция), «специальные» и «встроенный» (основные функции и операторы), «среда», «S4» (некоторые объекты S4) и другие, которые вряд ли будут видны пользователю уровень («символ», «pairlist», «обещание», «язык», «символ», «...», "any", "expression", "externalptr", "bytecode" и "weakref").

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

Режимы имеют тот же набор имен, что и типы (см. Typeof), за исключением того, что типы "integer" и "double" возвращаются как "numeric". типы «special» и «builtin» возвращаются как «function». Тип «символ» называется режим «имя». тип "language" возвращается как "(" или "call".

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

В списке есть список классов:

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

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

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

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

Заключение

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

...