R中的with和within有什么区别?

16

在我的研究中,我总是使用“with”而不是“within”,但是我最初认为它们是一样的。刚刚我把“with”错打成“within”,结果返回的结果非常不同。我想知道为什么。

我正在使用plyr包中的棒球数据,因此首先通过加载库来使用:

 require(plyr)

然后,我想选择所有 id 为 "ansonca01" 的行。起初,正如我所说,我使用了 "within" 并执行如下函数:

within(baseball, baseball[id=="ansonca01", ])

我得到了非常奇怪的结果,基本上包括了所有内容:

       id year stint team lg   g  ab   r   h X2b X3b hr rbi  sb cs  bb  so ibb hbp sh sf gidp
4     ansonca01 1871     1  RC1     25 120  29  39  11   3  0  16   6  2   2   1  NA  NA NA NA   NA
44    forceda01 1871     1  WS3     32 162  45  45   9   4  0  29   8  0   4   0  NA  NA NA NA   NA
68    mathebo01 1871     1  FW1     19  89  15  24   3   1  0  10   2  1   2   0  NA  NA NA NA   NA
99    startjo01 1871     1  NY2     33 161  35  58   5   1  1  34   4  2   3   0  NA  NA NA NA   NA
102   suttoez01 1871     1  CL1     29 128  35  45   3   7  3  23   3  1   1   0  NA  NA NA NA   NA
106   whitede01 1871     1  CL1     29 146  40  47   6   5  1  21   2  2   4   1  NA  NA NA NA   NA
113    yorkto01 1871     1  TRO     29 145  36  37   5   7  2  23   2  2   9   1  NA  NA NA NA   NA
.........

然后我使用“with”代替“within”。

 with(baseball, baseball[id=="ansonca01",])

并且获得了我期望的结果

      id year stint team lg   g  ab   r   h X2b X3b hr rbi sb cs  bb so ibb hbp sh sf gidp
4    ansonca01 1871     1  RC1     25 120  29  39  11   3  0  16  6  2   2  1  NA  NA NA NA   NA
121  ansonca01 1872     1  PH1     46 217  60  90  10   7  0  50  6  6  16  3  NA  NA NA NA   NA
276  ansonca01 1873     1  PH1     52 254  53 101   9   2  0  36  0  2   5  1  NA  NA NA NA   NA
398  ansonca01 1874     1  PH1     55 259  51  87   8   3  0  37  6  0   4  1  NA  NA NA NA   NA
525  ansonca01 1875     1  PH1     69 326  84 106  15   3  0  58 11  6   4  2  NA  NA NA NA   NA

我在R环境中输入help(with)查看了with和within的文档,并得到以下信息:

with是一个通用函数,它在从数据构建的本地环境中评估表达式expr。该环境具有调用者的环境作为其父级。这对于简化对建模函数的调用非常有用。(注意:如果数据已经是一个环境,则使用其现有的父级。)

请注意,expr内部的赋值发生在构造环境中而不是用户的工作区。

within类似,但它会在评估expr后检查环境并对数据进行相应修改(如果创建了无法存储在数据帧中的对象,则数据帧情况下可能会失败),并返回它。 within可用作transform的替代方法。

根据这些差异的说明,我不明白为什么我使用这样一个简单的操作却获得了不同的结果。有人有想法吗?


5
在像这样的语句中为什么要使用 withwith(baseball,baseball[id=="ansonca01",])?你只需要使用 baseball[baseball$id=="ansonca01",] - Thomas
within 返回完整的数据框。在 within 内部进行提取并没有什么意义。尝试在 withwithin 内部创建一个新变量,你会更清楚地看到差异。 - Thomas
@Thomas 你好,当然我知道可以使用baseball[baseball$id=="ansonca01",]进行子集操作,实际上我一直都是这样做的。今天我只是想尝试一些不同的东西,结果发现with和within在这种简单操作下返回了不同的结果。我想要理解为什么!!!我了解到"within"是用于创建新变量的,但这并不能回答我在这里提出的问题,即为什么这两个函数在子集操作中会导致不同的结果? - nan
2
@Thomas 问题在于这个例子中我只指定了一个子集条件,即[id=="ansonca01"]。如果我想在不同的列上指定更多条件,那么我就必须多次输入"baseball$somevariable",或者进行attach/detach操作,使用with/within可能是节省努力的好方法。 - nan
2
实际上,问题的第一部分“我想选择所有行…”需要调用subset(baseball, id=="ansonca01") - baptiste
显示剩余2条评论
3个回答

21

我发现简单的例子经常有助于突出差异。比如:

df <- data.frame(a=1:5,b=2:6)
df
  a b
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

with(df, {c <- a + b; df;} )
  a b
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

within(df, {c <- a + b; df;} )
# equivalent to: within(df, c <- a + b)
# i've just made the return of df explicit 
# for comparison's sake
  a b  c
1 1 2  3
2 2 3  5
3 3 4  7
4 4 5  9
5 5 6 11

16

文档非常清楚关于“with”和“within”这两个词的语义和返回值(并且很好地匹配了日常用语的意思):

值:

对于“with”,计算出的“expr”的值。 对于“within”,修改后的对象。

由于您的代码未修改baseball内部任何内容,因此将返回未修改的baseball。另一方面,“with”不返回对象,它返回expr

以下是一个示例,其中expr修改了对象:

> head(within(cars, speed[dist < 20] <- 1))
  speed dist
1     1    2
2     1   10
3     1    4
4     7   22
5     1   16
6     1   10

我觉得这就是我问题的答案。我确实读了你从文档中“引用”的内容,但是当我阅读那个句子(对于‘with’,是评估‘expr’的值。对于‘within’,是修改后的对象)时,我并没有完全理解。你的解释帮助我重新思考,并且现在它有意义了。我明白了,“with”返回表达式的结果。“within”则会返回原始对象,除非你进行了修改。谢谢! - nan
你能否告诉我在 within 的情况下,r如何检测对象是否被修改,是通过检查 <- 的存在吗? - SIslam
@SIslam 它并不会检查那个。它只是评估您通过within传递的表达式,并在数据框内执行评估。 - Konrad Rudolph

4

如上所述,with返回最后一个计算表达式的值。对于一行代码非常方便,例如:

with(cars, summary(lm (speed ~ dist)))

但不适合发送多个表达式。

我经常发现within在操作data.framelist(或data.table)时非常有用,因为我觉得它的语法易于阅读。

我认为通过添加这方面的使用示例可以改善文档,例如:

df1 <- data.frame(a=1:3,
              b=4:6,
              c=letters[1:3])
## library("data.table")  
## df1 <- as.data.table(df1)
df1 <- within(df1, {
    a <- 10:12
    b[1:2] <- letters[25:26]
    c <- a
})
df1

提供
    a b  c
1: 10 y 10
2: 11 z 11
3: 12 6 12

并且。
df1 <- as.list(df1)
df1 <- within(df1, {
    a <- 20:23
    b[1:2] <- letters[25:26]
    c <- paste0(a, b)
})
df1

提供
$a
[1] 20 21 22 23

$b
[1] "y" "z" "6"

$c
[1] "20y" "21z" "226" "23y"

请注意,methods("within") 仅适用于以下对象类型:
  • within.data.frame
  • within.list
  • (如果加载了该包,则还包括 within.data.table)。
其他包可能定义了其他方法。
也许有些出乎意料的是,在已定义的环境中操作变量时,withwithin 通常不是合适的选择...
针对评论 - 没有 within.environment 方法。使用 with 需要您在调用函数时在环境中,这在某种程度上违背了我的目的,例如:
df1 <- as.environment(df1)
## with(df1, ls()) ## Error
assign("ls", ls, envir=df1)
with(df1, ls())

1
你能解释一下你最后一句话的意思吗? - Ben Bolker

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