`уровни
в ответ на другой вопрос, @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 ответа:
ответы здесь хороши, но они упускают важный момент. Позвольте мне попробовать и описать его.
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<-
вызов делает все-таки).