R自我引用

37

在R中,我经常会这样做:

adataframe[adataframe$col==something]<-adataframe[adataframe$col==something)]+1

这种方法有点冗长和繁琐。是否有一种方法可以引用要更改的对象,例如:

adataframe[adataframe$col==something]<-$self+1 

?


5
请查看此处的 inc 函数:http://www.statisticsblog.com/2011/10/waiting-in-line-waiting-on-r/ - G. Grothendieck
3
你是否在寻找 adataframe <- transform(adataframe,something=something+1) 这段代码?它并不是自我引用,但比你之前的代码要简洁易懂一些。 - Ben Bolker
首先,我们会省略掉冗余的"which"。 - hadley
4个回答

34

尝试使用data.table包及其:=操作符。它非常快且非常简短。

DT[col1==something, col2:=col3+1]
第一部分 col1==something 是子集。你可以随意放置任何内容,并像变量一样使用列名;即,无需使用$。然后第二部分col2:=col3+1将 RHS 分配到该子集内的 LHS 中,其中列名可以赋值为变量一样。:=是按引用赋值。不会复制任何对象,因此比<-=withintransform更快。

另外,在即将推出的 v1.8.1 版本中,j 的语法允许在 j 中使用 := 的最终目标之一是将其与 by 结合起来,参见问题:当应该在 data.table 中使用 := 运算符时

更新:这确实在2012年7月发布了(:= 按群组)。


而且可能比我的产品经过更加严格的测试。 - IRTFM
问题:您可以仅使用DT[col1 ...]而不是DT [DT $ col1 ...]吗? - Tomas
@TomasT:可以。请注意,如果col1被索引了,“DT[col1="a",]”会执行向量扫描,而“DT["a",]”会执行二分查找。 - Ryogi
3
:= 不是一种基本的语言操作符。在 R 中未被使用且未定义,但可被定义。 - Matt Dowle
@Tomas:从非常实际的意义上讲,data.table包确实重新定义了“[”运算符,并且这是语言允许的,因为它期望基于对象类别分派通用方法。此外,还有一种创建中缀运算符的机制:?"%in% - IRTFM
显示剩余2条评论

15
你应该更加关注Gabor Grothendieck(不仅是在这个例子中)。Matt Asher博客中引用的inc函数可以满足你所需的所有功能:
(显然的扩展同样有效。)

你应该关注Gabor Grothendeick,他在这个领域很有建树。(并且不只是在这个例子里如此)Matt Asher博客中提到的inc函数可以实现你所要求的一切:

(而且显然的扩展也能起作用。)

add <- function(x, inc=1) {
   eval.parent(substitute(x <- x + inc))
 }
# Testing the `inc` function behavior

编辑:在第一条评论中,我暂时对缺乏批准感到恼火,但我接受了添加另一个函数参数的挑战。只需提供数据框架的一部分作为参数,它仍将将值范围增加一。到目前为止只对中缀二元运算符进行了轻微测试,但我认为它与任何只接受两个参数的函数一起使用都没有问题:

transfn <- function(x, func="+", inc=1) {
   eval.parent(substitute(x <- do.call(func, list(x , inc)))) }

(承认有罪:从传统的R角度来看,为赋值返回值似乎“不太正确”)以下是inc函数的早期测试:

df <- data.frame(a1 =1:10, a2=21:30, b=1:2)
 inc <- function(x) {
   eval.parent(substitute(x <- x + 1))
 }

#---- examples===============>

> inc(df$a1)  # works on whole columns
> df
   a1 a2 b
1   2 21 1
2   3 22 2
3   4 23 1
4   5 24 2
5   6 25 1
6   7 26 2
7   8 27 1
8   9 28 2
9  10 29 1
10 11 30 2
> inc(df$a1[df$a1>5]) # testing on a restricted range of one column
> df
   a1 a2 b
1   2 21 1
2   3 22 2
3   4 23 1
4   5 24 2
5   7 25 1
6   8 26 2
7   9 27 1
8  10 28 2
9  11 29 1
10 12 30 2

> inc(df[ df$a1>5, ])  #testing on a range of rows for all columns being transformed
> df
   a1 a2 b
1   2 21 1
2   3 22 2
3   4 23 1
4   5 24 2
5   8 26 2
6   9 27 3
7  10 28 2
8  11 29 3
9  12 30 2
10 13 31 3
# and even in selected rows and grepped names of columns meeting a criterion
> inc(df[ df$a1 <= 3, grep("a", names(df)) ])
> df
   a1 a2 b
1   3 22 1
2   4 23 2
3   4 23 1
4   5 24 2
5   8 26 2
6   9 27 3
7  10 28 2
8  11 29 3
9  12 30 2
10 13 31 3

1
如果我需要执行相同的操作多次,这是很棒的,但是如果我只需要执行一次该操作,定义增加函数就不值得花费时间了。 - LostLin
10
有些人就是无法满足。 - IRTFM
1
笑,如果这是我能得到的最好的,那我就接受吧。我只是好奇是否有更简单的解决方案,因为我不太熟悉所有现有的R函数。 - LostLin
1
甚至可以使用“…”参数列表来推广transfn。考虑到您的用户名,这是一个值得探究的适当领域。 - IRTFM

6

以下是您可以做的。假设您有一个数据框:

df = data.frame(x = 1:10, y = rnorm(10))

如果你想把所有的 y 增加 1,可以很容易地使用 transform 实现。

df = transform(df, y = y + 1)

还有 df <- within(df, y<- y+1),它比 transform 更通用。 - hatmatrix
这仍然是在某个子集上执行的时候有点烦人。例如,如果我只想增加小于1的y值,我仍然需要编写df=transfrom(df, y[y<1]<-y[y<1]+1 - LostLin

6
我倾向于(假定子集在行上)。
ridx <- adataframe$col==something
adataframe[ridx,] <- adataframe[ridx,] + 1

希望这个翻译能够不依赖于任何花哨/易碎的解析,对所执行的操作相当表达清晰,而且不会太啰嗦。它还倾向于将行分成易于人类阅读的单元,使用标准习语也很吸引人——对我来说,R 的词汇和特点已经足够多了。


如果有SO.fortunes包的话,最后一句话可以被添加进去。 - IRTFM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接