`уровни


в ответ на другой вопрос, @Marek опубликовал следующее решение: https://stackoverflow.com/a/10432263/636656

dat <- structure(list(product = c(11L, 11L, 9L, 9L, 6L, 1L, 11L, 5L, 
                                  7L, 11L, 5L, 11L, 4L, 3L, 10L, 7L, 10L, 5L, 9L, 8L)), .Names = "product", row.names = c(NA, -20L), class = "data.frame")

`levels<-`(
  factor(dat$product),
  list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
  )

который производит как выход:

 [1] Generic Generic Bayer   Bayer   Advil   Tylenol Generic Advil   Bayer   Generic Advil   Generic Advil   Tylenol
[15] Generic Bayer   Generic Advil   Bayer   Bayer  

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

res <- `levels<-`(
  factor(dat$product),
  list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
  )

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

4 103

4 ответа:

ответы здесь хороши, но они упускают важный момент. Позвольте мне попробовать и описать его.

R является функциональным языком и не любит мутировать свои объекты. Но это позволяет присваивать операторы, используя функции замены:

levels(x) <- y

эквивалентно

x <- `levels<-`(x, y)

фокус в том, что это переписывание выполняется <-; это не сделано levels<-. levels<- это просто обычная функция, которая принимает вход и дает выход; это ничего не мутирует.

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

levels(factor(x)) <- y

и

factor(x) <- `levels<-`(factor(x), y)

и

x <- `factor<-`(x, `levels<-`(factor(x), y))

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

но тогда, как только вы определили функции замены, такие как levels<-, вы получаете еще один, неожиданный неожиданный результат: у вас не просто есть возможность выполнять задания, у вас есть удобная функция, которая принимает фактор и выдает другой фактор с разными уровнями. Там действительно нет ничего "назначение" об этом!

Итак, код, который вы описываете, просто использует эту другую интерпретацию levels<-. Я признаю, что имя levels<- немного сбивает с толку потому что это предполагает назначение, но это не то, что происходит. Код просто настраивает своего рода конвейер:

  • Начнем с dat$product

  • преобразовать его в фактор

  • изменить уровни

  • магазин, что в res

лично я думаю, что строка кода красива ;)

причина этой " магии "заключается в том, что форма" назначение " должна иметь реальную переменную для работы. А то factor(dat$product) не был назначен ни на что.

# This works since its done in several steps
x <- factor(dat$product)
levels(x) <- list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
x

# This doesn't work although it's the "same" thing:
levels(factor(dat$product)) <- list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
# Error: could not find function "factor<-"

# and this is the magic work-around that does work
`levels<-`(
  factor(dat$product),
  list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
  )

нет колдовства, вот как определяются функции назначения (sub). levels<- немного отличается, потому что это примитив (sub)присваивать атрибуты фактора, а не сами элементы. Есть много примеров такого типа функции:

`<-`              # assignment
`[<-`             # sub-assignment
`[<-.data.frame`  # sub-assignment data.frame method
`dimnames<-`      # change dimname attribute
`attributes<-`    # change any attributes

другие бинарные операторы тоже можно назвать так:

`+`(1,2)  # 3
`-`(1,2)  # -1
`*`(1,2)  # 2
`/`(1,2)  # 0.5

теперь, когда вы это знаете, что-то вроде этого должно действительно взорвать ваш ум:

Data <- data.frame(x=1:10, y=10:1)
names(Data)[1] <- "HI"              # How does that work?!? Magic! ;-)

для пользовательского кода мне интересно, почему такие языковые манипуляции используются так? Вы спрашиваете, что это за магия, и другие указали, что вы вызываете функцию замены, которая имеет имя levels<-. Для большинства людей это магия и на самом деле предполагается использовать levels(foo) <- bar.

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

в этих условиях within() является идеальной функцией для использования. Вы, естественно, хотели бы написать

levels(product) <- bar

в R, но конечно product не существует как объект. within() обходит это, потому что он устанавливает среду, в которой вы хотите запустить свой R-код, и оценивает ваше выражение в этой среде. Назначение возвращаемого объекта из вызова в преуспевает в том, чтобы правильно измененный фрейм данных.

вот пример (вам не нужно создавать новый datX - я просто делаю это, чтобы промежуточные шаги оставались в конце)

## one or t'other
#dat2 <- transform(dat, product = factor(product))
dat2 <- within(dat, product <- factor(product))

## then
dat3 <- within(dat2, 
               levels(product) <- list(Tylenol=1:3, Advil=4:6, 
                                       Bayer=7:9, Generic=10:12))

что дает:

> head(dat3)
  product
1 Generic
2 Generic
3   Bayer
4   Bayer
5   Advil
6 Tylenol
> str(dat3)
'data.frame':   20 obs. of  1 variable:
 $ product: Factor w/ 4 levels "Tylenol","Advil",..: 4 4 3 3 2 1 4 2 3 4 ...

я изо всех сил пытаюсь увидеть, как конструкции, подобные той, которую вы показываете, полезны в большинстве случаев - если вы хотите изменить данные, измените данные, не создавайте другую копию и не изменяйте ее (что все levels<- вызов делает все-таки).