默认名称连接

tim*_*kas 7 attributes r vector

如果我有一个命名向量

v <- c(a = 1, b = 2)
Run Code Online (Sandbox Code Playgroud)

我添加它们

s <- v[2] + v[1]
Run Code Online (Sandbox Code Playgroud)

结果是一个长度为1的向量,其元素被命名为算术中的第一个元素,这里是"b".您可以使用双括号删除此行为.

无论如何,如果我然后尝试使用c()创建一个新的命名向量

v <- c(v, sum = s)
Run Code Online (Sandbox Code Playgroud)

sum元素的结果名称不是"sum",而是"sum.b".

这种行为是不可取的,因为我特别指出我希望这个元素被命名为sum.

如果相反,我添加这样的元素:

v["sum"] <- s
Run Code Online (Sandbox Code Playgroud)

我得到了理想的行为.

为什么R连接对象的名称和使用提供的名称c(),为什么这与使用括号中的新名称添加元素不同?这不是要问如何摆脱这种行为(我可以用双括号或者这样做unname()),但是它背后的原则是什么,以及在其他情况下我可以期待这种情况发生?

Ian*_*ley 1

运算符和组合函数的属性保存规则

`+` <- function(e1, e2) {}通过操作符函数(例如)和组合函数( )实现的转换c <- function(...) {},设计者尝试保留对象属性(最常见的是names)。

操作员功能

对于运算符函数,规则如下:

  • 规则 1:如果 length(answer) == length(e1) & length(answer) < length(e2) 则使用 e1 属性。

  • 规则 2:如果 length(answer) == length(e2) & length(answer) < length(e1) 则使用 e2 属性。

  • 规则 3:如果 length(answer) == length(e1) & length(answer) == length(e2) 则同时使用 e1 和 e2 属性,但 e1 属性优先。

首先,设置命名和未命名向量。使用多元素对象,因为它们比问题中使用的单元素对象更好地演示规则的应用。

print(a <- setNames(1:2, sprintf("a%s", 1:2)))
# a1 a2 
#  1  2
print(b <- setNames(1:4, sprintf("b%s", 1:4))) 
# b1 b2 b3 b4
#  1  2  3  4
print(u <- 1:2) # unnamed
# [1] 1 2
print(x <- setNames(1:4, rep("x", 4))) 
# x x x x
# 1 2 3 4
Run Code Online (Sandbox Code Playgroud)

然后是一些例子:

#' rule 1
names(a + b[1]) # [1] "a1" "a2"
names(b + a[1]) # [1] "b1" "b2" "b3" "b4"
names(u + a[1]) # NULL

#' rule 2
names(a[1] + b) # [1] "b1" "b2" "b3" "b4"
names(b[1] + a) # [1] "a1" "a2"
names(u[1] + a) # [1] "a1" "a2"

#' rule 3
names(a + b[1:2]) # [1] "a1" "a2"
names(b[1:2] + a) # [1] "b1" "12"
names(u + a)      # [1] "a1" "a2" ## e1 is unnamed so use e2's names
Run Code Online (Sandbox Code Playgroud)

运算符函数不保留参数名称,实际上似乎完全忽略它们(仅顺序很重要),即使是函数定义中的参数名称(e1e2)。

names(`+`(v1 = u, v2 = 0))      # NULL
names(`+`(e2 = a, e1 = b[1:2])) # [1] "a1" "a2" ## despite e1 being b[1:2]
Run Code Online (Sandbox Code Playgroud)

组合功能

对于组合函数,首先要注意的是,参数名称不会像运算符函数那样被忽略,并且参数及其元素都可以命名。规则如下:

  • 规则 1:如果参数未命名且其元素未命名,则不使用任何名称。

  • 规则 2:如果参数未命名但其元素已命名,则使用元素名称。

  • 规则 3:如果参数已命名且其元素未命名,则对于长度 == 1 的参数使用参数名称,对于长度 > 1 的参数使用带有连续数字后缀的参数名称。

  • 规则 4:如果参数已命名且其元素已命名,则使用由点连接的名称。

首先是一个显示参数命名的函数:

c... <- function(...) {match.call(expand.dots=FALSE)$...}
Run Code Online (Sandbox Code Playgroud)

一些例子:

# rules 1 and 2
names(c...(u, a)) # NULL, all arguments unnamed
names(c(u, a))    # c("", "", "a1", "a2")

# rules 3 and 4
names(c...(v1 = u[1], v2 = u, v3 = a)) # c("v1", "v2", "v3"), all arguments named
names(c(v1 = u[1], v2 = u, v3 = a))    # c("v1", "v21", "v22", "v3.a1", "v3.a2")

# all rules
names(c...(u, v1 = u, a, v2 = a)) # c("", "v1", "", "v2") ## some arguments named
names(c(u, v1 = u, a, v2 = a))    # c("", "", "v11", "v12", "a1", "a2", "v2.a1", "v2.a2")
Run Code Online (Sandbox Code Playgroud)

应该注意的是,规则的设计目的是尽可能保留属性,但其目的并不是将名称创建为唯一标识符。

 # ambiguities (rules 2 and 4)
 names(c(x, v1 = x)) # c("x", "x", "x", "x", "v1.x", "v1.x", "v1.x", "v1.x")
Run Code Online (Sandbox Code Playgroud)

当运算符和组合函数一起使用时,每个函数的规则将按计算顺序应用。因此c(a) + c(b),在 中(相当于 )`+`(c(a), c(b)),首先应用组合函数规则。而在 中(a + b),与 等价c(`+`(a, b)),首先应用运算符函数规则。

# c() first, so argument names used
names(c(v1=a) + c(v2=b))    # c("v2.b1", "v2.b2", "v2.b3", "v2.b4")
names(`+`(c(v1=a),c(v2=b))) # c("v2.b1", "v2.b2", "v2.b3", "v2.b4") 

# `+`() first, so argument names ignored
names(c((v1=a) + (v2=b))) # c("b1", "b2", "b3", "b4")
names(c(`+`(v1=a, v2=b))) # c("b1", "b2", "b3", "b4")
Run Code Online (Sandbox Code Playgroud)

问题和答案

问题是,(1)“为什么 R 连接对象的名称和使用 c() 提供的名称”,以及 (2)“为什么这与在括号中使用新名称添加元素不同?”

  1. 运算符和组合函数的原则是通过应用上面给出的规则,以最有意义(且可预测)的方式保留对象属性(包括参数和元素名称)。所有参数和对象的所有属性不可能完整保留,因此应用 (a) 优先级、(b) 顺序数字后缀和 (c) 连接来保留尽可能多的最合适的信息。

  2. 命名元素的直接赋值会同时分配值和属性,在这种情况下,无需保留两个对象或参数和对象的属性,因此上述注意事项均不适用。