`seviyeleri <-` (Bu ne büyücülük?


114

Başka bir soruya yanıt olarak @Marek şu çözümü yayınladı: 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ı olarak üreten:

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

Bu sadece bir vektörün çıktısıdır, bu yüzden onu saklamak için daha da kafa karıştırıcı olanı yapabilirsiniz:

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

Açıkçası bu, seviyeler işlevine bir tür çağrı, ancak burada ne yapıldığı hakkında hiçbir fikrim yok. Bu tür büyücülük için kullanılan terim nedir ve bu alanda büyü yeteneğimi nasıl artırabilirim?


1
Ayrıca names<-ve vardır [<-.
huon

1
Ayrıca, diğer soruda bunu merak ettim ama sormadım: structure(...)yapı için sadece yerine herhangi bir neden var data.frame(product = c(11L, 11L, ..., 8L))mı? (Orada bir sihir meydana geliyorsa, onu da kullanmak isterim!)
huon

2
Bu, "levels<-"fonksiyona bir çağrıdır :, bunun bir function (x, value) .Primitive("levels<-")tür X %in% Ykısaltmasıdır "%in%"(X, Y).
BenBarnes

2
@dbaupp Tekrarlanabilir örnekler için çok kullanışlı: stackoverflow.com/questions/5963269/…
Ari B. Friedman

8
Yapıcı olmadığı gerekçesiyle neden birisinin bunu kapatmak için oy kullandığını bilmiyorum Q'nun çok net bir cevabı var: Örnekte kullanılan sözdiziminin anlamı nedir ve bu R'de nasıl çalışır?
Gavin Simpson

Yanıtlar:


104

Buradaki cevaplar güzel, ancak önemli bir noktayı kaçırıyorlar. Deneyip tarif edeyim.

R işlevsel bir dildir ve nesnelerini değiştirmeyi sevmez. Ancak değiştirme işlevlerini kullanarak atama ifadelerine izin verir:

levels(x) <- y

eşdeğerdir

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

İşin püf noktası, bu yeniden yazma işlemi şu şekilde yapılır <-; tarafından yapılmaz levels<-. levels<-sadece bir girdi alan ve bir çıktı veren normal bir işlevdir; hiçbir şeyi değiştirmez.

Bunun bir sonucu, yukarıdaki kurala göre <-yinelemeli olması gerektiğidir:

levels(factor(x)) <- y

dır-dir

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

dır-dir

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

Bu saf-işlevsel dönüşümün (görevin gerçekleştiği sona kadar), bir görevin zorunlu bir dilde olacağına eşdeğer olması çok güzel. Doğru hatırlıyorsam, işlevsel dillerdeki bu yapıya mercek denir.

Ancak daha sonra, değiştirme işlevlerini tanımladıktan sonra, levels<-başka, beklenmedik bir beklenmedik durumla karşılaşırsınız: yalnızca atama yapma yeteneğine sahip değilsiniz, bir faktörü alan ve farklı seviyelerde başka bir faktör veren kullanışlı bir işleve sahipsiniz. Bunda gerçekten hiçbir "görev" yok!

Yani, tarif ettiğiniz kod sadece bu diğer yorumdan yararlanıyor levels<-. İsmin levels<-biraz kafa karıştırıcı olduğunu itiraf ediyorum çünkü bir görev öneriyor, ama olan bu değil. Kod basitçe bir tür ardışık düzen kuruyor:

  • İle başla dat$product

  • Bir faktöre dönüştürün

  • Seviyeleri değiştir

  • İçinde sakla res

Şahsen, bu kod satırının güzel olduğunu düşünüyorum;)


33

Büyü yok, bu sadece (alt) atama işlevlerinin tanımlanma şeklidir. levels<-biraz farklıdır çünkü bir faktörün özniteliklerini atamak (alt), öğelerin kendilerine değil. Bu tür işlevlerin birçok örneği vardır:

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

Diğer ikili operatörler de şu şekilde çağrılabilir:

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

Artık bunu bildiğinize göre, bunun gibi bir şey gerçekten aklınızı başınızdan almalı:

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

1
Fonksiyonları normal yoldan değil de bu şekilde çağırmanın ne zaman mantıklı olduğu hakkında biraz daha açıklayabilir misiniz? Bağlantılı soruda @ Marek'in örneği üzerinde çalışıyorum, ancak daha açık bir açıklamaya sahip olmak yardımcı olacaktır.
Drew Steen

4
@DrewSteen: Kod netliği / okunabilirlik nedenleriyle, bunun hiçbir zaman mantıklı olmadığını söyleyebilirim çünkü `levels<-`(foo,bar)ile aynıdır levels(foo) <- bar. @ Marek örneğini kullanmak: `levels<-`(as.factor(foo),bar)ile aynıdır foo <- as.factor(foo); levels(foo) <- bar.
Joshua Ulrich

Güzel liste. Bunun levels<-gerçekten sadece bir kısaltma olduğunu düşünmüyor musunuz attr<-(x, "levels") <- value, yoksa en azından muhtemelen ilkel hale gelene ve C koduna devredilene kadar öyleydi.
IRTFM

30

Bu "sihrin" nedeni, "atama" formunun üzerinde çalışmak için gerçek bir değişkene sahip olması gerektiğidir. Ve factor(dat$product)hiçbir şeye atanmamıştı.

# 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)
  )

+1 Bence önce faktöre dönüştürmek, ardından seviyeleri a within()ve transform()çağrı ile değiştirmek, bu şekilde değiştirilen nesne döndürülür ve atanırsa daha temiz olur.
Gavin Simpson

4
@GavinSimpson - Katılıyorum, sadece sihri açıklıyorum, savunmuyorum ;-)
Tommy

16

Kullanıcı kodu için bu tür dil manipülasyonlarının neden böyle kullanıldığını merak ediyorum? Bunun ne sihir olduğunu sorarsınız ve diğerleri, adı taşıyan değiştirme işlevini aradığınızı belirttiler levels<-. Çoğu insan için bu sihirdir ve gerçekten amaçlanan kullanımdır levels(foo) <- bar.

Gösterdiğiniz kullanım durumu farklıdır çünkü productküresel ortamda mevcut değildir, bu nedenle yalnızca çağrının yerel ortamında var olur, levels<-bu nedenle yapmak istediğiniz değişiklik kalıcı olmaz - yeniden atama yoktur dat.

Bu durumlarda, within() kullanılacak ideal işlevdir. Doğal olarak yazmak istersin

levels(product) <- bar

R'de ama tabii ki productnesne olarak mevcut değil. within()bunun üstesinden gelir çünkü R kodunuzu çalıştırmak istediğiniz ortamı ayarlar ve o ortamdaki ifadenizi değerlendirir. Çağrıdan dönüş nesnesinin atanması, within()uygun şekilde değiştirilmiş veri çerçevesinde başarılı olur.

İşte bir örnek (yeni oluşturmanıza gerek yok datX- bunu sadece ara adımlar sonunda kalsın diye yapıyorum)

## 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))

Hangi verir:

> 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 ...

Gösterdiğiniz gibi yapıların çoğu durumda nasıl yararlı olduğunu görmeye çalışıyorum - verileri değiştirmek istiyorsanız, verileri değiştirin, başka bir kopya oluşturmayın ve bunu değiştirin ( levels<-sonuçta tüm çağrı budur ).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.