`ระดับ <-` (นี่คือคาถาอะไร?


115

ในคำตอบสำหรับคำถามอื่น @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)
  )

เห็นได้ชัดว่านี่เป็นการเรียกใช้ฟังก์ชันระดับ แต่ฉันไม่รู้ว่ากำลังทำอะไรอยู่ที่นี่ คำศัพท์สำหรับเวทมนตร์ประเภทนี้คืออะไรและฉันจะเพิ่มความสามารถทางเวทมนตร์ของฉันในโดเมนนี้ได้อย่างไร


1
นอกจากนี้ยังมีและnames<- [<-
huon

1
นอกจากนี้ฉันสงสัยเกี่ยวกับคำถามนี้ในคำถามอื่น แต่ไม่ได้ถาม: มีเหตุผลอะไรในการstructure(...)สร้างแทนที่จะเป็นเพียงแค่data.frame(product = c(11L, 11L, ..., 8L))? (ถ้ามีเวทมนตร์เกิดขึ้นฉันก็อยากจะใช้มันด้วย!)
huon

2
เป็นการเรียกใช้"levels<-"ฟังก์ชัน: function (x, value) .Primitive("levels<-")sort of like X %in% Yเป็นคำย่อของ"%in%"(X, Y).
BenBarnes

2
@dbaupp มีประโยชน์มากสำหรับตัวอย่างที่ทำซ้ำได้: stackoverflow.com/questions/5963269/…
Ari B. Friedman

8
ฉันไม่รู้ว่าทำไมมีคนโหวตให้ปิดเรื่องนี้ว่าไม่สร้างสรรค์? Q มีคำตอบที่ชัดเจนมาก: ความหมายของไวยากรณ์ที่ใช้ในตัวอย่างคืออะไรและทำงานอย่างไรใน R?
Gavin Simpson

คำตอบ:


105

คำตอบที่นี่เป็นสิ่งที่ดี แต่ไม่มีประเด็นสำคัญ ให้ฉันลองอธิบายดู

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

เป็นเรื่องสวยงามที่การเปลี่ยนแปลงที่ใช้งานได้จริงนี้ (จนถึงจุดสิ้นสุดที่งานจะเกิดขึ้น) เทียบเท่ากับสิ่งที่งานมอบหมายจะเป็นภาษาที่จำเป็น ถ้าจำไม่ผิดโครงสร้างนี้ในภาษาที่ใช้งานได้เรียกว่าเลนส์

แต่เมื่อคุณกำหนดฟังก์ชันทดแทนเช่นlevels<-คุณจะได้รับโชคลาภที่ไม่คาดคิดอีกอย่างหนึ่ง: คุณไม่เพียง แต่มีความสามารถในการมอบหมายงานเท่านั้น แต่คุณมีฟังก์ชันที่มีประโยชน์ซึ่งรับปัจจัยหนึ่งและให้ปัจจัยอื่นที่มีระดับต่างๆ ไม่มี "งานมอบหมาย" เกี่ยวกับเรื่องนี้จริงๆ!

ดังนั้นโค้ดที่คุณอธิบายเป็นเพียงการใช้ประโยชน์จากการตีความอื่น ๆlevels<-นี้ ฉันยอมรับว่าชื่อlevels<-นี้ค่อนข้างสับสนเพราะเป็นการแนะนำงาน แต่นี่ไม่ใช่สิ่งที่เกิดขึ้น รหัสนี้เป็นเพียงการตั้งค่าไปป์ไลน์:

  • เริ่มกับ dat$product

  • แปลงเป็นตัวประกอบ

  • เปลี่ยนระดับ

  • เก็บไว้ใน res

โดยส่วนตัวผมคิดว่าบรรทัดของโค้ดนั้นสวยงาม;)


33

ไม่มีเวทมนตร์นั่นเป็นเพียงวิธีการกำหนดฟังก์ชันการกำหนด (ย่อย) levels<-แตกต่างกันเล็กน้อยเนื่องจากเป็นแบบดั้งเดิมที่ (ย่อย) กำหนดคุณลักษณะของปัจจัยไม่ใช่องค์ประกอบเอง มีตัวอย่างมากมายของฟังก์ชันประเภทนี้:

`<-`              # 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! ;-)

1
คุณช่วยอธิบายเพิ่มเติมเล็กน้อยได้ไหมว่าเมื่อใดที่ควรเรียกใช้ฟังก์ชันด้วยวิธีนี้แทนที่จะเป็นวิธีปกติ ฉันกำลังดำเนินการตามตัวอย่างของ @ Marek ในคำถามที่เชื่อมโยง แต่จะช่วยให้มีคำอธิบายที่ชัดเจนยิ่งขึ้น
Drew Steen

4
@DrewSteen: เพื่อความชัดเจนของรหัส / เหตุผลในการอ่านฉันจะบอกว่ามันไม่สมเหตุสมผลเพราะ`levels<-`(foo,bar)มันเหมือนกับlevels(foo) <- bar. การใช้ตัวอย่างของ @ Marek: `levels<-`(as.factor(foo),bar)เหมือนกับfoo <- as.factor(foo); levels(foo) <- barไฟล์.
Joshua Ulrich

รายการที่ดี คุณไม่คิดว่าlevels<-เป็นเพียงการจดชวเลขจริง ๆattr<-(x, "levels") <- valueหรืออย่างน้อยก็อาจเป็นไปได้จนกว่ามันจะกลายเป็นแบบดั้งเดิมและส่งมอบให้กับ C-code
IRTFM

30

เหตุผลสำหรับ "เวทมนตร์" นั้นก็คือแบบฟอร์ม "มอบหมาย" ต้องมีตัวแปรที่แท้จริงในการทำงาน และ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)
  )

+1 ฉันคิดว่าการแปลงเป็นตัวประกอบก่อนจะดีกว่าจากนั้นแทนที่ระดับด้วยการเรียกwithin()และการtransform()เรียกวัตถุที่แก้ไขตามนี้จะถูกส่งคืนและกำหนด
Gavin Simpson

4
@GavinSimpson - ฉันเห็นด้วยฉันแค่อธิบายเวทมนตร์ฉันไม่ปกป้องมัน ;-)
ทอมมี่

16

สำหรับรหัสผู้ใช้ฉันสงสัยว่าเหตุใดจึงใช้การปรับแต่งภาษาดังกล่าว? คุณขอให้สิ่งที่วิเศษนี้และคนอื่น ๆ levels<-ได้ออกมาชี้ว่าคุณกำลังเรียกใช้ฟังก์ชันทดแทนที่มีชื่อ levels(foo) <- barสำหรับคนส่วนใหญ่นี้คือความมหัศจรรย์และจริงๆวัตถุประสงค์การใช้งานคือ

ใช้ในกรณีที่คุณแสดงที่แตกต่างกันเพราะproductไม่ได้อยู่ในสภาพแวดล้อมของโลกดังนั้นจึงเท่านั้นที่เคยอยู่ในสภาพแวดล้อมในท้องถิ่นของการเรียกร้องให้levels<-ดังนั้นการเปลี่ยนแปลงที่คุณต้องการที่จะทำให้ไม่ได้ยังคงมีอยู่ - datมีการโอนไม่มี

ในสถานการณ์เหล่านี้within() เป็นฟังก์ชันที่เหมาะสำหรับการใช้งาน คุณย่อมปรารถนาที่จะเขียน

levels(product) <- bar

ใน R แต่แน่นอนว่าproductไม่มีอยู่ในรูปของวัตถุ within()หลีกเลี่ยงสิ่งนี้ได้เนื่องจากตั้งค่าสภาพแวดล้อมที่คุณต้องการรันโค้ด R ของคุณและประเมินนิพจน์ของคุณภายในสภาพแวดล้อมนั้น การกำหนดอ็อบเจ็กต์ส่งคืนจากการเรียกเพื่อให้within()ประสบความสำเร็จในเฟรมข้อมูลที่แก้ไขอย่างถูกต้อง

นี่คือตัวอย่าง (คุณไม่จำเป็นต้องสร้างใหม่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<-โทรทั้งหมดที่เกิดขึ้นในภายหลัง )

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.