`level <-`(这是什么巫术?

Ari*_*man 110 types r levels

在回答另一个问题时,@ Mayk发布了以下解决方案: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)
  )
Run Code Online (Sandbox Code Playgroud)

哪个产生输出:

 [1] Generic Generic Bayer   Bayer   Advil   Tylenol Generic Advil   Bayer   Generic Advil   Generic Advil   Tylenol
[15] Generic Bayer   Generic Advil   Bayer   Bayer  
Run Code Online (Sandbox Code Playgroud)

这只是矢量的打印输出,所以要存储它你可以做得更加困惑:

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

显然这是对level函数的某种调用,但我不知道这里做了什么.这种巫术的术语是什么,我如何在这个领域增加我的魔法能力?

Owe*_*wen 100

这里的答案很好,但它们缺少重要的一点.让我试着描述一下.

R是一种函数式语言,不喜欢改变它的对象.但它确实允许赋值语句,使用替换函数:

levels(x) <- y
Run Code Online (Sandbox Code Playgroud)

相当于

x <- `levels<-`(x, y)
Run Code Online (Sandbox Code Playgroud)

诀窍是,这种重写是通过<-; 它不是由levels<-.levels<-只是一个常规函数,它接受输入并给出输出; 它不会改变任何东西.

其结果之一是,根据上述规则,<-必须是递归的:

levels(factor(x)) <- y
Run Code Online (Sandbox Code Playgroud)

factor(x) <- `levels<-`(factor(x), y)
Run Code Online (Sandbox Code Playgroud)

x <- `factor<-`(x, `levels<-`(factor(x), y))
Run Code Online (Sandbox Code Playgroud)

这种纯粹的功能转换(直到结束发生的最终结束)等同于命令式语言中的赋值,这有点漂亮.如果我没记错的话,这种功能语言的结构被称为镜头.

但是,一旦你定义了替换函数levels<-,你会得到另一个意想不到的意外收获:你不仅能够进行任务,你还有一个方便的函数,它可以考虑一个因素,并给出另一个不同级别的因素.关于它真的没什么"任务"!

所以,你所描述的代码只是利用了对其他的解释levels<-.我承认这个名字levels<-有点混乱,因为它暗示了一项任务,但这不是正在发生的事情.代码只是设置一种管道:

  • 从...开始 dat$product

  • 将其转换为一个因子

  • 改变水平

  • 存放在 res

就个人而言,我认为这行代码很漂亮;)


Jos*_*ich 31

没有巫术,这就是如何定义(子)赋值函数. levels<-有点不同,因为它是一个原始的(子)分配因子的属性,而不是元素本身.有很多这种功能的例子:

`<-`              # assignment
`[<-`             # sub-assignment
`[<-.data.frame`  # sub-assignment data.frame method
`dimnames<-`      # change dimname attribute
`attributes<-`    # change any attributes
Run Code Online (Sandbox Code Playgroud)

其他二元运算符也可以这样调用:

`+`(1,2)  # 3
`-`(1,2)  # -1
`*`(1,2)  # 2
`/`(1,2)  # 0.5
Run Code Online (Sandbox Code Playgroud)

既然你知道,这样的事情真的会让你大吃一惊:

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

  • @DrewSteen:出于代码清晰度/可读性的原因,我认为它永远不会有意义,因为`\`levels < - \`(foo,bar)`与`levels(foo)< - bar`相同.使用@Marek的例子:`\`levels < - \`(as.factor(foo),bar)`与`foo < - as.factor(foo)相同; level(foo)< - bar`. (4认同)

Tom*_*mmy 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)
  )
Run Code Online (Sandbox Code Playgroud)

  • @GavinSimpson - 我同意,我只解释魔法,我不捍卫它;-) (4认同)

Rei*_*son 16

对于用户代码,我想知道为什么会使用这样的语言操作?你问这是什么魔法,其他人已经指出你正在调用具有该名称的替换函数levels<-.对于大多数人来说,这是神奇的,真正的预期用途是levels(foo) <- bar.

您显示的用例是不同的,因为product在全局环境中不存在,因此它只存在于调用的本地环境中,levels<-因此您想要进行的更改不会持续存在 - 没有重新分配dat.

在这些情况下,within() 是理想的使用功能.你自然希望写

levels(product) <- bar
Run Code Online (Sandbox Code Playgroud)

在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))
Run Code Online (Sandbox Code Playgroud)

这使:

> 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 ...
Run Code Online (Sandbox Code Playgroud)

我很难看到你所展示的结构在大多数情况下是如何有用的 - 如果你想改变数据,改变数据,不要创建另一个副本并改变它(这毕竟是所有的levels<-调用都在做) ).