`水平<-`(这是什么巫术?

在回答另一个问题时,@Marek发布了以下解决scheme: 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 

这只是一个vector的打印输出,所以要存储它,你可以做更多的混淆:

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

显然这是对关卡function的一种调用,但我不知道这里正在做什么。 这种巫术的术语是什么,如何提高我在这个领域的魔法能力?

这里的答案是好的,但是他们缺less重要的一点。 让我试着描述一下。

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

 levels(x) <- y 

相当于

 x <- `levels<-`(x, y) 

诀窍是,这个重写是通过<- ; 它不是由levels<-完成的。 levels<-只是一个正则函数,它接受一个input并给出一个输出; 它不会改变任何东西。

其中的一个后果就是根据上述规则,必须recursion:

 levels(factor(x)) <- y 

 factor(x) <- `levels<-`(factor(x), y) 

 x <- `factor<-`(x, `levels<-`(factor(x), y)) 

这种纯粹function的转换(直到最后,分配发生的地方)等同于命令式语言中的分配是相当美丽的。 如果我没有记错的话,这个函数式语言的结构被称为镜头。

但是,一旦你定义了替代函数如levels<- ,你会得到另一个意想不到的意外收获:你不仅有能力进行任务分配,还有一个方便的函数来接受一个因素,并给出另一个因素不同的层次。 关于它的确没有任何“任务”!

所以,你所描述的代码就是利用这个levels<-其他解释levels<- 。 我承认,名称levels<-有点令人困惑,因为它表明了一个任务,但这不是发生的事情。 代码只是build立一种pipe道:

  • dat$product开始

  • 把它转换成一个因子

  • 更改级别

  • 将其存储在res

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

这个“魔术”的原因是“分配”forms必须有一个真正的variables来处理。 而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) ) 

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

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

对于用户代码,我不明白为什么要使用这种语言操作? 你问这是什么魔术,而其他人指出你正在调用名称levels<-的replace函数。 对于大多数人来说,这是神奇的,真正的目的是使用levels(foo) <- bar

你显示的用例是不同的,因为product不存在于全局环境中,所以它只存在于调用levels<-的本地环境中levels<-因此,你想要做的改变不会持久 – 没有重新分配dat

在这种情况下, within()是理想的使用function。 你当然希望写

 levels(product) <- bar 

在R但当然product不存在作为一个对象。 within()解决了这个问题,因为它build立了你希望运行你的R代码的环境,并在那个环境中评估你的expression式。 将调用的返回对象分配给within()可以正确修改dataframe。

这里是一个例子(你不需要创build新的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 ... 

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