W odpowiedzi na kolejne pytanie @Marek zamieścił następujące rozwiązanie: 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)
)
Który produkuje jako wynik:
[1] Generic Generic Bayer Bayer Advil Tylenol Generic Advil Bayer Generic Advil Generic Advil Tylenol
[15] Generic Bayer Generic Advil Bayer Bayer
To tylko wydruk wektora, więc aby go zapisać, możesz zrobić jeszcze bardziej zagmatwane:
res <- `levels<-`(
factor(dat$product),
list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
)
Oczywiście jest to wywołanie funkcji poziomów, ale nie mam pojęcia, co się tutaj dzieje. Jaki jest termin określający ten rodzaj czarów i jak mogę zwiększyć swoje magiczne zdolności w tej dziedzinie?
names<-
i[<-
.structure(...)
konstrukcji zamiast po prostudata.frame(product = c(11L, 11L, ..., 8L))
? (Jeśli dzieje się tam jakaś magia, też chciałbym nią władać!)"levels<-"
funkcjifunction (x, value) .Primitive("levels<-")
:, coś w rodzaju podobnegoX %in% Y
skrótu"%in%"(X, Y)
.Odpowiedzi:
Odpowiedzi tutaj są dobre, ale brakuje w nich ważnego punktu. Spróbuję to opisać.
R jest językiem funkcjonalnym i nie lubi mutować swoich obiektów. Ale pozwala na instrukcje przypisania, używając funkcji zastępczych:
jest równa
Rzecz w tym, że przepisywanie odbywa się przez
<-
; nie jest to zrobione przezlevels<-
.levels<-
jest zwykłą funkcją pobierającą dane wejściowe i wyświetlającą dane wyjściowe; niczego nie mutuje.Jedną z konsekwencji tego jest to, że zgodnie z powyższą zasadą
<-
musi być rekurencyjny:jest
jest
To całkiem piękne, że ta czysto funkcjonalna transformacja (aż do samego końca, gdzie następuje zadanie) jest równoważna temu, czym byłoby zadanie w języku imperatywnym. Jeśli dobrze pamiętam, ten konstrukt w językach funkcjonalnych nazywa się soczewką.
Ale potem, kiedy już zdefiniujesz funkcje zastępcze, takie jak
levels<-
, otrzymujesz kolejną, nieoczekiwaną gratkę: nie tylko masz możliwość wykonywania przypisań, masz przydatną funkcję, która przyjmuje czynnik i wydaje inny czynnik o różnych poziomach. Naprawdę nie ma w tym nic „przydziału”!Tak więc kod, który opisujesz, po prostu wykorzystuje tę inną interpretację
levels<-
. Przyznaję, że nazwalevels<-
jest trochę zagmatwana, ponieważ sugeruje zadanie, ale nie o to chodzi. Kod po prostu tworzy rodzaj potoku:Zacząć od
dat$product
Zamień go na współczynnik
Zmień poziomy
Przechowuj to w
res
Osobiście uważam, że ta linia kodu jest piękna;)
źródło
Żadnej magii, tak właśnie definiuje się (pod) funkcje przypisania.
levels<-
jest trochę inna, ponieważ (pod) przypisywanie atrybutów czynnika jest prymitywne, a nie samych elementów. Istnieje wiele przykładów tego typu funkcji:Inne operatory binarne również można nazwać w ten sposób:
Teraz, gdy już to wiesz, coś takiego powinno naprawdę zaskoczyć:
źródło
`levels<-`(foo,bar)
jest takie samo jaklevels(foo) <- bar
. Na przykładzie @ Marek:`levels<-`(as.factor(foo),bar)
to to samo, cofoo <- as.factor(foo); levels(foo) <- bar
.levels<-
jest to tylko skrótattr<-(x, "levels") <- value
, a przynajmniej prawdopodobnie tak było, dopóki nie został przekształcony w prymityw i przekazany kodowi C.Powodem tej „magii” jest to, że formularz „przypisania” musi mieć rzeczywistą zmienną, nad którą można pracować. I
factor(dat$product)
nie był do niczego przypisany.źródło
within()
itransform()
wywołanie, gdy w ten sposób zmodyfikowany obiekt zostanie zwrócony i przypisany.W przypadku kodu użytkownika zastanawiam się, dlaczego takie manipulacje językowe są tak używane? Pytasz, co to za magia, a inni wskazywali, że wywołujesz funkcję zastępczą, która ma nazwę
levels<-
. Dla większości ludzi jest to magia i tak naprawdę jest to zamierzone zastosowanielevels(foo) <- bar
.Pokazany przypadek użycia jest inny, ponieważ
product
nie istnieje w środowisku globalnym, więc zawsze istnieje tylko w lokalnym środowisku wywołania,levels<-
więc zmiana, którą chcesz wprowadzić, nie zachowuje się - nie było ponownego przypisaniadat
.W takich okolicznościach
within()
jest to idealna funkcja do użycia. Naturalnie chciałbyś pisaćw R, ale oczywiście
product
nie istnieje jako obiekt.within()
omija ten problem, ponieważ konfiguruje środowisko, na którym chcesz uruchomić kod R, i ocenia Twoje wyrażenie w tym środowisku. Przypisanie obiektu zwracanego z wywołania do wwithin()
ten sposób powiedzie się w odpowiednio zmodyfikowanej ramce danych.Oto przykład (nie musisz tworzyć nowego
datX
- po prostu to robię, aby kroki pośrednie pozostały na końcu)Co daje:
Z trudem widzę, jak konstrukcje takie jak ta, którą pokazujesz, są przydatne w większości przypadków - jeśli chcesz zmienić dane, zmienić dane, nie tworzyć kolejnej kopii i zmieniać to (co jest wszystkim, co
levels<-
robi połączenie ).źródło