【问题标题】:Use of Tilde (~) and period (.) in R在 R 中使用波浪号 (~) 和句点 (.)
【发布时间】:2020-10-10 18:20:34
【问题描述】:

我正在使用 Hadley 的 R4DS 书籍使用 tidyverse 和 purrr 进行循环,对于波浪号 ~ 符号和句点符号的确切用法有点困惑。

因此,当编写 for 循环或使用 map() 时,而不是写出 function(),看来您可以使用波浪号代替 ~。

这仅适用于 for 循环吗?

所以如下...

models <- mtcars %>% 
  split(.$cyl) %>% 
  map(~lm(mpg ~ wt, data = .))

此外,我被告知的句点可以用来“指代当前列表元素”。但我很困惑这意味着什么。这是否意味着,只有在循环时,句点才表示它指的是列表中被循环的元素?它与管道有什么区别?管道时,您将一行的结果管道传输到下一行代码。

所以在上面的例子中,mtcars 使用 split() 传递到第二行,但使用了句点。为什么?

下面的案例总结了我的困惑:

x <- c(1:10)

detect(x, ~.x > 5)

使用检测功能,找到第一个匹配,我想我可以使用

detect(x, x >5)

但我收到一条错误消息,说 x >5 不是函数。所以我加了一个波浪号

detect(x, ~ x > 5)

并得到一个错误,它需要一个 TRUE 或 FALSE,而不是 10。所以如果你添加一个句点

detect(x, ~.x >5) 

突然间它像循环一样工作了。那么 ~ 和 . 的关系/用法是什么?这里和如何。与简单的管道相比?

【问题讨论】:

标签: r for-loop r-formula


【解决方案1】:

这种整体称为tidyverse 非标准评估 (NSE)。您可能发现~ 也是used in formulas 表示左侧依赖于右侧。

tidyverse NSE 中,~ 表示function(...)。因此,这两个表达式是等价的。

x %>% detect(function(...) ..1 > 5)
#[1] 6

x %>% detect(~.x > 5)
#[1] 6

~ 自动将函数的每个参数分配给..x.y;和..1..2..3 特殊符号。请注意,只有第一个参数变为 .

map2(1, 2, function(x,y) x + y)
#[[1]]
#[1] 3

map2(1, 2, ~.x + .y)
#[[1]]
#[1] 3

map2(1, 2, ~..1 + ..2)
#[[1]]
#[1] 3

map2(1, 2, ~. + ..2)
#[[1]]
#[1] 3

map2(1, 2, ~. + .[2])
#[[1]]
#[1] NA

当有很多变量时,这种自动赋值非常有用。

mtcars %>% pmap_dbl(~ ..1/..4)
# [1] 0.19090909 0.19090909 0.24516129 0.19454545 0.10685714 0.17238095 0.05836735 0.39354839 0.24000000 0.15609756
#[11] 0.14471545 0.09111111 0.09611111 0.08444444 0.05073171 0.04837209 0.06391304 0.49090909 0.58461538 0.52153846
#[21] 0.22164948 0.10333333 0.10133333 0.05428571 0.10971429 0.41363636 0.28571429 0.26902655 0.05984848 0.11257143
#[31] 0.04477612 0.19633028

但除了我上面提到的所有特殊符号之外,参数还分配给...。就像所有 R 一样,... 有点像参数的命名列表,所以你可以将它与with 一起使用:

mtcars %>% pmap_dbl(~ with(list(...), mpg/hp))
# [1] 0.19090909 0.19090909 0.24516129 0.19454545 0.10685714 0.17238095 0.05836735 0.39354839 0.24000000 0.15609756
#[11] 0.14471545 0.09111111 0.09611111 0.08444444 0.05073171 0.04837209 0.06391304 0.49090909 0.58461538 0.52153846
#[21] 0.22164948 0.10333333 0.10133333 0.05428571 0.10971429 0.41363636 0.28571429 0.26902655 0.05984848 0.11257143
#[31] 0.04477612 0.19633028

另一种思考为什么会这样是因为data.frames 只是一个带有一些行名的list

a <- list(a = c(1,2), b = c("A","B"))
a
#$a
#[1] 1 2
#$b
#[1] "A" "B"
attr(a,"row.names") <- as.character(c(1,2))
class(a) <- "data.frame"
a
#  a b
#1 1 A
#2 2 B

【讨论】:

  • 特殊符号 .x、.y、..1、..2、..3 和 .只是参考符号?根据该示例,它们似乎只是在代码中引用了前面的值。在 map(1,2, ~.x + .y) 中,.x 引用第​​一个值,.y 引用第二个值。然后在 mtcars 示例中,它几乎就像它的子集,因为 ..1 指的是 mtcars 的第一列。我理解对了吗?
  • 你明白了。要记住的另一件事是 . 是在函数环境中评估的,而不是在对 detect 的调用中。这就是为什么. 指的是x 的当前值而不是整个向量。
  • 在 detect() 示例中,. 指的是 x 的当前值,而不是整个向量。那是因为在检测函数中内置了一个 for 循环吗?接下来,在上面的示例中,.1 和 .2 在 map(1,2, ~.x + .y) 示例中是“直接”引用的,但在 mtcars 示例中,它们更多的是子集。我如何知道符号何时用于从原始值中提取子集,何时用于直接引用该值。我想在我们有 map(mtcars, 1, 2, ~ ) 的情况下,我将如何引用 mtcars 中的第三列与第三值 (2)?
  • 抱歉,澄清一下,. 指的是函数中第一个参数的值。请参阅(function(x) x &gt; 5)(x)help(detect) 的评估。至于你关于map(mtcars, 1, 2, ~ ) 的问题,这并没有达到你的预期,因为map 只接受一个列表或向量来应用。
  • 我明白了。最后一个问题!当我评估 map(mtcars, ~ ..1/2) 时,结果是每列的值除以 2。而当我使用 pmap(mtcars, ~ ..1 /2) 时,它是第一列除以 2。所以它在 pmap 的情况下,第一列的 ..1 子集看起来像,但在 map 的情况下却没有。地图案例中发生了什么? pmap(mtcars, ~ ..1 /2 )
猜你喜欢
  • 2020-05-08
  • 2019-04-09
  • 2013-02-05
  • 2019-10-30
  • 2018-02-27
  • 2021-09-15
  • 2019-05-17
  • 2016-03-27
  • 1970-01-01
相关资源
最近更新 更多