在回答另一个问题时,@ 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)
)
产生输出:
[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)
)
显然这是对level函数的某种调用,但我不知道在这里做了什么。这种巫术的术语是什么,我如何在这个领域增加我的魔法能力?
答案 0 :(得分:95)
这里的答案很好,但它们缺少重要的一点。让我试着描述一下。
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
就个人而言,我认为代码行很漂亮;)
答案 1 :(得分: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)
)
答案 2 :(得分:29)
没有巫术,这就是如何定义(子)赋值函数。 levels<-
有点不同,因为(sub)分配因子的属性而不是元素本身是原始的。有很多这种功能的例子:
`<-` # 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! ;-)
答案 3 :(得分: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<-
来电正在做。)