在回答另一个问题时,@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)
)
型
显然这是对levels函数的某种调用,但我不知道这里做了什么。这种魔法的术语是什么,我如何在这个领域增加我的魔法能力?
4条答案
按热度按时间ny6fqffe1#
这里的答案很好,但他们忽略了一个重要的问题。让我试着描述一下。
R是一种函数式语言,不喜欢改变它的对象。但它允许赋值语句,使用替换函数:
字符串
相当于
型
诀窍是,这个重写是由
<-
完成的;它不是由levels<-
完成。levels<-
只是一个接受输入并给出输出的常规函数;它不会变异任何东西。根据上面的规则,
<-
必须是递归的:型
是
型
是
型
这种纯函数式的转换(直到最后,赋值发生的地方)相当于命令式语言中的赋值,这有点漂亮。在函数式语言中,这种结构被称为lens。在一些编程语言中使用透镜可能会很尴尬,但在R中它们只是工作。
但是,一旦你定义了像
levels<-
这样的替换函数,你会得到另一个意想不到的意外收获:你不仅有赋值的能力,你还有一个方便的函数,它接受一个因子,并给出另一个不同水平的因子。真的没有什么“任务”!因此,您所描述的代码只是利用了
levels<-
的另一种解释。我承认levels<-
这个名字有点令人困惑,因为它暗示了一个赋值,但这不是正在发生的事情。代码只是建立了一种管道:dat$product
开始res
中我个人认为那行代码很漂亮;)
fiei3ece2#
没有魔法,这就是如何定义(子)赋值函数。
levels<-
有一点不同,因为它是一个原语,用于(子)分配因子的属性,而不是元素本身。这种类型的函数有很多例子:字符串
其他二元运算符也可以这样调用:
型
现在你知道了,像这样的事情真的应该让你大吃一惊:
型
htrmnn0y3#
之所以会有这种“魔力”,是因为“赋值”形式必须有一个真实的的变量来处理。
factor(dat$product)
没有被分配给任何东西。字符串
liwlm1x94#
对于用户代码,我想知道为什么要使用这样的语言操作?您问这是什么魔法,其他人指出您正在调用名为
levels<-
的替换函数。对于大多数人来说,这是一个魔术,真正的预期用途是levels(foo) <- bar
。您展示的用例是不同的,因为
product
不存在于全局环境中,所以它只存在于调用levels<-
的本地环境中,因此您想要进行的更改不会持久-没有重新分配dat
。在这些情况下,
within()
是理想的函数。你自然会想写字符串
但是
product
并不作为对象存在。within()
解决了这个问题,因为它设置了您希望运行R代码的环境,并在该环境中计算表达式。因此,将调用的返回对象分配给within()
会在正确修改的 Dataframe 中成功。下面是一个示例(您不需要创建新的
datX
-我只是这样做,以便中间步骤保留在最后)型
其给出:
型
我很难理解像你展示的这样的结构在大多数情况下是如何有用的--如果你想改变数据,改变数据,不要创建另一个副本并改变它(毕竟这是
levels<-
调用所做的一切)。