`levels <-` (이건 무슨 마법인가요?


114

다른 질문에 대한 답변으로 @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))있습니까? (만약 마법이
일어나면 저도

2
"levels<-"함수에 대한 호출입니다 . function (x, value) .Primitive("levels<-"), 일종의 X %in% Y약어입니다 "%in%"(X, Y).
BenBarnes

2
@dbaupp 재현 가능한 예제에 매우 편리합니다. stackoverflow.com/questions/5963269/…
Ari B. Friedman

8
왜 누군가가 건설적이지 않은 것으로 종결하도록 투표했는지 모르겠습니다. Q는 매우 명확한 답을 가지고 있습니다. 예제에서 사용 된 구문의 의미는 무엇이며 R에서 어떻게 작동합니까?
Gavin Simpson

답변:


104

여기에 대한 답변은 좋지만 중요한 점이 누락되었습니다. 시도하고 설명해 보겠습니다.

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. 마렉의 예를 @ 사용 : `levels<-`(as.factor(foo),bar)와 동일합니다 foo <- as.factor(foo); levels(foo) <- bar.
조슈아 울리히

좋은 목록입니다. levels<-은 실제로를 약어로 생각하지 마십시오 attr<-(x, "levels") <- value. 또는 최소한 원시 형식으로 변환되어 C 코드로 넘겨지기 전까지는 아마 그럴 것입니다.
IRTFM 2014

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 먼저 factor로 변환 한 다음 a within()transform()call을 통해 수준을 교체하는 것이 더 깨끗할 것이라고 생각합니다 . 이렇게 수정 된 개체가 반환되고 할당됩니다.
Gavin Simpson

4
@GavinSimpson-동의합니다. 마법에 대해서만 설명합니다. 방어하지 않습니다 ;-)
Tommy

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.