В ответ на другой вопрос @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)
)
Ясно, что это какой-то вызов функции уровней, но я понятия не имею, что здесь делается. Как называется этот вид колдовства и как мне увеличить свои магические способности в этой области?
names<-
и[<-
.structure(...)
конструкции вместо простоdata.frame(product = c(11L, 11L, ..., 8L))
? (Если там творится какая-то магия, я бы тоже ею стал владеть!)"levels<-"
функции:,function (x, value) .Primitive("levels<-")
вроде какX %in% Y
это сокращение для"%in%"(X, Y)
.Ответы:
Ответы здесь хорошие, но в них упускается важный момент. Позвольте мне попытаться описать это.
R - это функциональный язык, и он не любит видоизменять свои объекты. Но он позволяет операторы присваивания с использованием функций замены:
эквивалентно
Хитрость в том, что это переписывание выполняется с помощью
<-
; это не делаетсяlevels<-
.levels<-
это просто обычная функция, которая принимает вход и выдает результат; он ничего не мутирует.Одним из следствий этого является то, что, согласно приведенному выше правилу,
<-
должно быть рекурсивным:является
является
Прекрасно, что это чисто функциональное преобразование (до самого конца, где происходит присваивание) эквивалентно тому, что присваивание было бы в императивном языке. Если я правильно помню, эта конструкция в функциональных языках называется линзой.
Но затем, как только вы определили функции замены, например
levels<-
, вы получите еще одну неожиданную удачу: у вас не просто есть возможность выполнять назначения, у вас есть удобная функция, которая принимает фактор и выдает другой фактор с разными уровнями. В этом нет ничего "поручения"!Итак, код, который вы описываете, просто использует эту другую интерпретацию
levels<-
. Я признаю, что это названиеlevels<-
немного сбивает с толку, потому что оно предполагает задание, но это не то, что происходит. Код просто настраивает своего рода конвейер:Начать с
dat$product
Преобразуйте это в коэффициент
Измените уровни
Храните это в
res
Лично я считаю эту строчку кода красивой;)
источник
Никакого колдовства, просто так определяются функции (под) присваивания.
levels<-
немного отличается, потому что это примитив для (под) назначения атрибутов фактора, а не самих элементов. Есть множество примеров такого типа функций:Так же могут вызываться и другие бинарные операторы:
Теперь, когда вы это знаете, что-то вроде этого должно действительно поразить вас:
источник
`levels<-`(foo,bar)
это то же самое, что иlevels(foo) <- bar
. Используя пример @Marek:`levels<-`(as.factor(foo),bar)
то же самое, что иfoo <- as.factor(foo); levels(foo) <- bar
.levels<-
это действительно просто сокращениеattr<-(x, "levels") <- value
, или, по крайней мере, вероятно, так было до тех пор, пока его не превратили в примитив и не передали в C-код.Причина этой «магии» в том, что форма «присваивания» должна иметь реальную переменную для работы. И
factor(dat$product)
ни к чему не приписывался.источник
within()
иtransform()
call, если таким образом измененный объект будет возвращен и назначен.Что касается пользовательского кода, мне интересно, почему такие языковые манипуляции так используются? Вы спрашиваете, что это за магия, а другие указывали, что вы вызываете функцию замены, у которой есть имя
levels<-
. Для большинства людей это магия и действительно так и по назначениюlevels(foo) <- bar
.Показанный вами вариант использования отличается, поскольку
product
он не существует в глобальной среде, поэтому он всегда существует только в локальной среде вызова,levels<-
поэтому изменение, которое вы хотите сделать, не сохраняется - не было переназначенияdat
.В этих обстоятельствах
within()
это идеальная функция для использования. Вы, естественно, захотите написатьв R, но, конечно
product
, не существует как объект.within()
обходит это, потому что он устанавливает среду, в которой вы хотите запустить свой код R, и оценивает ваше выражение в этой среде.within()
Таким образом, присвоение возвращаемого объекта из вызова успешно измененному кадру данных.Вот пример (вам не нужно создавать новое
datX
- я просто делаю это, чтобы промежуточные шаги остались в конце)Который дает:
Я изо всех сил пытаюсь понять, насколько полезны конструкции, подобные той, которую вы показываете, в большинстве случаев - если вы хотите изменить данные, изменить данные, не создавайте еще одну копию и не меняйте ее (в
levels<-
конце концов, это все, что делает вызов ).источник