在R语言中,恒等函数的实际用途是什么?

39

基本的 R 语言定义了一个 identity 函数,它是一个平凡的恒等函数,返回其自变量(引用自 ?identity)。

它的定义如下:

identity <- function (x){x}

这个如此琐碎的函数有什么用处呢?为什么它会被包含在基础R中?


1
我曾在curve(identity(x))的上下文中看过它的使用(而不是稍微比较晦涩的curve(x*1)curve(x+0)... - Ben Bolker
@BenBolker为什么不直接使用curve(x)呢? - Andrie
1
尝试一下 - 它不起作用(Error in eval(expr, envir, enclos) : could not find function "x"),因为 curve 使用有趣的评估规则... - Ben Bolker
1
前几个答案都暗示了函数式编程。关于R和函数式编程的一些有用问题:http://stackoverflow.com/q/4874867/602276,http://stackoverflow.com/q/6167791/602276和https://dev59.com/nXE95IYBdhLWcg3wn_Vr。 - Andrie
8个回答

18

虽然不了解R语言,但在函数式编程中,常常会将一个函数作为参数传递给另一个函数。在这种情况下,常数函数(对于任何参数返回相同的值)和恒等函数的作用类似于乘法中的0和1,可以这么说。


2
请你能否再解释一下?我明白可以将函数作为参数传递,但是您所说的起到了0和1的作用是什么意思? - Andrie
@Andrie - 关于0和1:想象乘法。0n = 0,1n = n。 - Ingo
@Ingo 我熟悉身份的数学概念。我也使用了许多在R中使用传递函数作为参数的函数(applyddplyaggregateouter和其他大量函数)。但是我从未需要过自己的identify函数。所以我的问题仍然是:在函数式编程中,为什么需要一个identify函数?它的实际用途是什么?PS.您的答案不必特别针对R,因为这似乎是一个更通用的概念。 - Andrie
2
这种东西的实际用途可以是例如,作为“转换”类型参数的默认值。这类似于将“乘法因子”类型参数的默认值设置为1.0。在两种情况下,缺省值的效果相当于无操作。不必显式指定这一点(比如通过设置布尔do_not_transform),而是隐含地作为参数的属性(即作为身份运算符)简单明了。 - micans
1
@Andrie - 我可以举一个 Haskell 的例子。假设我有一个类型为 Maybe Int 的值,并且我对这个数字感兴趣。有一个函数 maybe d f x = case x of { Nothing -> d; Just i -> f i } 可以用来提取它。它给我应用一个函数到载荷或者默认值(如果 x 是 Nothing)的结果。所以,如果我想要保持该值不变,我只需传入 id(在 Haskell 中是身份函数的名称)。我知道这听起来对那些从未有过需要的人来说很牵强,但是也许你还没有编程得足够“功能化”。 - Ingo
显示剩余3条评论

13

我时常使用apply函数来运用它。

例如,你可以将t()写成:

dat <- data.frame(x=runif(10),y=runif(10))
apply(dat,1,identity)

       [,1]      [,2]      [,3]      [,4]      [,5]      [,6]       [,7]
x 0.1048485 0.7213284 0.9033974 0.4699182 0.4416660 0.1052732 0.06000952
y 0.7225307 0.2683224 0.7292261 0.5131646 0.4514837 0.3788556 0.46668331
       [,8]      [,9]      [,10]
x 0.2457748 0.3833299 0.86113771
y 0.9643703 0.3890342 0.01700427

9
换句话说,“identity”被用作“无操作”的开关,以真正利用调用函数的转换特性。不错。 - Andrie
@Andrie 没错。而且表达得比我能做到的更优美 :-) - Ari B. Friedman
这不是我说的,我只是在转述@tripleee删除的答案,我认为这个答案很有用,可以为其他答案提供背景信息。 - Andrie

9

一个在简单的代码库搜索中出现的用途,是作为tryCatch函数中最基本错误处理功能的便利。

tryCatch(...,error = identity)

这与原来的完全相同(哈!)。

tryCatch(...,error = function(e) e)

因此,这个处理程序会捕获错误消息,然后简单地返回它。

在这让我理解之前,我需要再思考一下。我一直避免学习trytryCatch的工作原理。感谢您提供的示例和参考资料。 - Andrie

6
不管它的价值如何,它位于基础包源代码中的“funprog.R”(函数式编程内容)中,并且在2008年被添加为“方便函数”:我可以想象(但无法立即给出示例!)在函数式编程方法(即使用“Filter”,“Reduce”,“Map”等)中会有一些情况,其中具有标识功能将非常方便...
r45063 | hornik | 2008-04-03 12:40:59 -0400 (Thu, 03 Apr 2008) | 2 lines

Add higher-order functions Find() and Position(), and convenience
function identity().

3
它也和“force”是同义词。 - hadley

2
远离函数式编程,identity 在 R 中还在另一个上下文中使用,即统计学。在这里,它用于指代 链接函数广义线性模型 中的身份。更多详情请参见 ?family?glm。以下是一个例子:
> x <- rnorm(100)
> y <- rpois(100, exp(1+x))
> glm(y ~x, family=quasi(link=identity))

Call:  glm(formula = y ~ x, family = quasi(link = identity))

Coefficients:
(Intercept)            x
      4.835        5.842

Degrees of Freedom: 99 Total (i.e. Null);  98 Residual
Null Deviance:      6713
Residual Deviance: 2993         AIC: NA

然而,在这种情况下,将其解析为字符串而不是函数会实现相同的效果:glm(y ~x, family=quasi(link="identity")) 编辑:如下面的评论中所指出的那样,函数base::identity并不是链接构造函数使用的函数,它只用于解析链接名称。(我不会删除这个答案,我会留下它来帮助澄清两者之间的区别。)

1
这在一个(现已删除的)答案中提到过... 在这种情况下,“identity”显然并不是指“base::identity”。请参见make.link中的代码;它只是匹配名称“identity”。 - joran
1
@nullglob:确实。答案是我写的,但我删除了它,因为它是不正确的。glm的family构造函数不使用identity函数,它们评估一个字符串(即使你可以传递未引用的参数)。 - Joris Meys

1

Here is usage example:

    Map<Integer, Long> m = Stream.of(1, 1, 2, 2, 3, 3)
            .collect(Collectors.groupingBy(Function.identity(),
                    Collectors.counting()));
    System.out.println(m);
    output:
    {1=2, 2=2, 3=2}

在这里,我们将int类型的值分组为一个int/count映射。Collectors.groupingBy接受一个函数。在我们的例子中,我们需要一个返回参数的函数。请注意,我们可以使用e->e lambda代替。

1
由于这个问题已经被浏览了8k次,即使它已经写了9年,更新一下也许是值得的。
在一篇名为“简单技巧调试Pipes(在magrittr、基础R或ggplot2中)”的博客文章中,作者指出identity()在不同类型的管道末尾非常有用。可以在这里找到带有示例的博客文章:https://rstats-tips.net/2021/06/06/simple-tricks-for-debugging-pipes-within-magrittr-base-r-or-ggplot2/ 如果管道链的书写方式是每个“管道”符号都在一行的末尾,那么您可以通过注释它来排除任何行的执行。除了最后一行。如果将identity()添加为最后一行,则永远不需要注释掉它。因此,您可以通过注释掉任何更改数据的行来暂时排除它们。

1
我就是这样使用它的:
fit_model <- function(lots, of, parameters, error_silently = TRUE) {

  purrr::compose(ifelse(test = error_silently, yes = tryNA, no = identity),
                 fit_model_)(lots, of, parameters)
}

tryNA <- function(expr) {
  suppressWarnings(tryCatch(expr = expr,
                            error = function(e) NA,
                            finally = NA))
}

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