0
点赞
收藏
分享

微信扫一扫

赋值表达式

赋值可能是所有编程语言中最基本的表达式了,它所做的就是将一个值(value)赋予

或者绑定到一个符号上,使得我们能够通过符号来访问这个值。

尽管编程语言之间有相似性,但 R 采用 <-符号来表示赋值。这和其他语言用 = 有点不

同,虽然在 R 中也可以用 = 进行赋值:

x <- 1

y <- c(1, 2, 3)

z <- list(x, y)

我们不需要在赋值前声明符号及其类型。如果环境中没有某个符号,赋值的同时就会

创建这个符号;反之,如果符号已经存在,也不会造成冲突,只是将值重新绑定到这个

符号上。

我们还可以使用一些其他可用且等效的运算符。x<-f(z)把 f(z) 绑定到符号 x 上,

相比之下,我们还可以用 -> 进行反向赋值:

2 -> x1

我们甚至可以将多个赋值运算符连接使用,使一组符号都取相同的值:

x3 <- x2 <- x1 <- 0

表达式 0 只被计算一次,就将相同的值同时赋予 3 个符号。为了验证它是如何运行的,

我们可以将 0 换成一个随机数生成器(random number generator):

x3 <- x2 <- x1 <- rnorm(1)

c(x1, x2, x3)

## [1] 1.585697 1.585697 1.585697

rnorm(1) 生成一个服从标准正态分布的随机数。如果上述赋值每次都重新调用该随

机数生成器,则每个符号会有不同的取值。然而,并没有发生这种情况。稍后,我们会解

释实际运行过程,这样你可以更好地理解。

像其他编程语言一样,= 也可以进行赋值:

x2 = c(1, 2, 3)

如果你熟悉其他流行编程语言,如 Python、Java 和 C#,可能就会发现,将 = 作为赋值

运算符几乎成为一个行业规范了,而 -> 这个需要两次键入操作的赋值符可能会让你感到不

方便。尽管二者都可以使用,且作为赋值运算符其作用是相同的,但是,谷歌的 R Style Guide

(https://google.github.io/styleguide/Rguide.xml#assignment)建议使用 <-而不是 =。

这里,对 <-和 = 的细微差别给出一个简单的说明。我们先创建一个带两个参数

的 f( ) 函数:

f <- function(input, data = NULL) {

cat("input:\n")

print(input)

cat("data:\n")

print(data)

}

这个函数就是输出两个参数的值。现在,我们用这个函数演示两个操作符的不同

之处:

x <- c(1, 2, 3)

y <- c("some", "text")

f(input = x)

## input:

## [1] 1 2 3

## data:

## NULL

上述代码同时使用了 <-和 =,但它们却扮演着不同的角色。前两行的 <-用作赋值运算

符,第 3 行的 = 为函数 f( ) 的参数 input 指定输入值。

具体来说,<-运算符计算它右侧的表达式 c(1,2,3),并将其值赋予左侧符号(变量)

x。而 = 并不用作赋值运算符,只是通过名称将函数参数与变量匹配起来。

我们知道 <-和 = 用作赋值运算符时,可以相互替代。因此,上述代码也可以等价地

写为:

x = c(1, 2, 3)

y = c("some", "text")

f(input = x)

## input:

## [1] 1 2 3

## data:

## NULL

这里我们只用了 = 运算符,但它有两种不同的用途:在前两行,= 执行赋值操作;而

在第 3 行,= 指定一个命名参数(将名为 input 的参数指定为符号 x)。

现在,让我们看看把每个 = 换成 <-将会发生什么:

x <- c(1, 2, 3)

y <- c("some", "text")

f(input <- x)

## input:

## [1] 1 2 3

## data:

## NULL

运行这段代码,你会发现输出结果是相似的。然而,如果你检查 R 的环境,就会看到

不同之处,环境中创建了一个新变量 input,取值为 c(1, 2, 3):

input

## [1] 1 2 3

那么,究竟发生了什么呢?实际上,第3 行代码做了两件事:第一,赋值操作 input <-x 将

一个新符号 input 引入到环境中,并取值为 x;第二,将 input 的值传递给函数 f( ) 的

第 1 个参数。换句话说,函数的第 1 个参数是通过位置而非名称建立的匹配关系。

为了更详细地进行描述,我们将进行更多试验。函数的标准用法如下:

f(input = x, data = y)

## input:

## [1] 1 2 3

## data:

## [1] "some" "text"

如果我们将上述两个 = 用 <-替代,结果看起来是一样的:

f(input <- x, data <- y)

## input:

## [1] 1 2 3

## data:

## [1] "some" "text"

使用 = 的那段代码,也可以交换函数参数的位置,而不改变结果:

f(data = y, input = x)

## input:

## [1] 1 2 3

## data:

## [1] "some" "text"

然而在这个例子中,如果我们将 = 换成 <-,那么 input 和 data 的值也交换了:

f(data <- y, input <- x)

## input:

## [1] "some" "text"

## data:

## [1] 1 2 3

以下代码与上述代码有相同的效果:

data <- y

input <- x

f(y, x)

## input:

## [1] "some" "text"

## data:

## [1] 1 2 3

这段代码相当于不仅调用了 f(y,x),还在当前环境中额外创建了两个不必要的变

量 data 和 input。

上述例子和试验给出了很清晰的演示。因此,为了减少歧义,可以用 <-或者 = 作为赋值

运算符,而仅用 = 为函数指定参数。为了提高 R 代码的可读性,正如 Google Style Guide 所建

议的,仅用 <-赋值,用 = 指定函数参数。

 使用带反引号的非标准名称

赋值运算符允许我们对一个变量(一个符号或名称)进行赋值。但是,直接赋值对符

号名称的格式有限制。名称只能包含从 a~z,A~Z 的字母(R 对大小写敏感)、下划线( _ )

和点( . ),不能有空格,也不能以下划线( _ )开头。

以下是一些有效的名称:

students <- data.frame()

us_population <- data.frame()

sales.2015 <- data.frame()

以下是一些违反命名规则的无效名称:

some data <- data.frame()

## Error: unexpected symbol in "some data"

_data <- data.frame()

## Error: unexpected input in "_"

Population(Millions) <- data.frame()

## Error in Population(Millions) <- data.frame() :

## object 'Millions' not found

上述名称以不同的方式违反了命名规则。some data 变量名中包含了空格,_data 以

下划线开头,Population(Millions) 不是一个符号名称而是一个函数调用。实践中,

很有可能一些无效的名称确实是一张数据表的列名,如第 3 个名称。

为了绕开对名称格式的限制,我们需要使用反引号来引用那些无效的名称,使其

有效:

`some data` <- c(1, 2, 3)

`_data` <- c(4, 5, 6)

`Population(Millions)` <- c(city1 = 50, city2 = 60)

要访问这些变量,也需要使用反引号,否则它们仍然会被认为是无效的:

`some data`

## [1] 1 2 3

`_data`

## [1] 4 5 6

`Population(Millions)`

## city1 city2

## 50 60

反引号可以用在任何创建符号的地方,包括函数:

`Tom's secret function` <- function(a, d) {

(a ^ 2 -d ^ 2) / (a ^ 2 + d ^ 2)

}

甚至适用于列表:

l1 <- list(`Group(A)` = rnorm(10), `Group(B)` = rnorm(10))

如果一个符号的名称无法被直接有效引用,我们也需要使用反引号来引用这个符号:

`Tom's secret function`(1,2)

## [1] -0.6

l1$`Group(A)`

## [1] -0.8255922 -1.1508127 -0.7093875 0.5977409 -0.5503219 -1.0826915

## [7] 2.8866138 0.6323885 -1.5265957 0.9926590

data.frame( ) 是一个例外:

results <- data.frame(`Group(A)` = rnorm(10), `Group(B)` = rnorm(10))

results

## Group.A. Group.B.

## 1 -1.14318956 1.66262403

## 2 -0.54348588 0.08932864

## 3 0.95958053 -0.45835235

## 4 0.05661183 -1.01670316

## 5 -0.03076004 0.11008584

## 6 -0.05672594 -2.16722176

## 7 -1.31293264 1.69768806

## 8 -0.98761119 -0.71073080

## 9 2.04856454 -1.41284611

## 10 0.09207977 -1.16899586

遗憾的是,尽管我们对含有其他符号的名称使用了反引号,生成的 data.frame 的变

量将那些符号用点代替,等价于使用函数 make.names( ),具体结果可以通过查

看 data.frame 的列名进行确认:

colnames(results)

## [1] "Group.A." "Group.B."

这常常发生在你导入一个数据表时,例如由某一实验得到的 CSV 数据:

ID,Category,Population(before),Population(after)

0,A,10,12

1,A,12,13

2,A,13,16

3,B,11,12

4,C,13,12

在 R 中,当你使用 read.csv( ) 读取 CSV 数据后,Population(before)

和 Population(after) 变量不会保持原来的名称,而是使用函数 make.names( ) 把

它们变成有效名称。我们运行以下命令,看看到底得到什么样的名称:

make.names(c("Population(before)", "Population(after)"))

## [1] "Population.before." "Population.after."

有时,我们并不希望程序自动执行这个操作。要使这个功能失效,可以在调

用 read.csv( )或 data.frame( ) 时,设置参数 check.names = FALSE:

results <- data.frame(

ID = c(0, 1, 2, 3, 4),

Category = c("A", "A", "A", "B", "C"),

`Population(before)` = c(10, 12, 13, 11, 13),

`Population(after)` = c(12, 13, 16, 12, 12),

stringsAsFactors = FALSE,

check.names = FALSE)

results

## ID Category Population(before) Population(after)

## 1 0 A 10 12

## 2 1 A 12 13

## 3 2 A 13 16

## 4 3 B 11 12

## 5 4 C 13 12

colnames(results)

## [1] "ID" "Category" "Population(before)"

## [4] "Population(after)"

在上述调用中,stringsAsFactors = FALSE 避免将字符向量转换为因子,

check.names=FALSE 避免对列名调用函数 make.names( )。设置了这两个参数,创建

的 data.frame 的变量将会最大限度地保留输入数据的格式。

正如上文提及的,要访问含特殊符号的列,需使用反引号引用列名:

results$`Population(before)`

## [1] 10 12 13 11 13

反引号使创建和访问带有特殊符号的变量成为可能,但这并不意味着我们建议使用这

样的命名方式。相反,它会降低代码可读性,也更易出错,并且难以使用针对严格命名规

则的外部工具。

总之,除非绝对必要,我们应避免使用反引号创建特殊变量名。




举报

相关推荐

0 条评论