2.2 数学运算符和向量
运算符+
执行加法,它还有一个特殊技巧:除了把两个数字相加之外,还可把两个向量相加。向量是数值的有序集,它在统计学中极其重要,因为通常的分析对象是整个数据集,而不仅是一条数据。
在上一章你已经看到,冒号运算符:
能创建一个从某个数值开始到另一个数值结束的序列。而c
函数则会把一系列的值拼接起来创建向量(这里c是concatenate的第一个字母,concatenate是一个拉丁词,意思是“把所有东西连接在一起”)。
R中的变量名是区分大小写的,因此下例中我们要留心一点。大写的C
函数与小写的c
函数作用完全不同1:
1有一些其他的名称冲突:filter
和Filter
,find
和Find
,gamma
和Gamma
,nrow
/ncol
和NROW
/NCOL
。因为R是一种不断演进而非一蹴而就的语言,这是一个令人遗憾的副作用。
1:5 + 6:10 #look, no loops!
## [1] 7 9 11 13 15
c(1, 3, 6, 10, 15) + c(0, 1, 3, 6, 10)
## [1] 1 4 9 16 25
小技巧
冒号运算符和
c
函数在R代码中几乎无处不在,好好练习使用它们吧。现在,试试创建你自己的向量。
如果要用C或Fortran语言编写代码,我们需要编写一个循环语句来为向量中的每个元素执行加法。R的向量化加法操作符把事情简单化了,使我们无需使用循环语句。2.5节将对向量作更多的介绍。
在R中,向量化有几种含义,其中最常见的含义是:运算符或函数能作用于向量中的每个元素,而无需显式地编写循环语句(这种内置的基于元素的隐式循环也远远快于显式地写循环语句)。向量化的第二个含义是:当一个函数把一个向量作为输入时,能计算汇总统计:
sum(1:5)
## [1] 15
median(1:5)
## [1] 3
向量化的第三种含义不太常见,即参数的向量化,例如当函数根据输入参数计算汇总统计时。sum
函数就是这样,不过这非常特殊。而median
函数则不是这样:
sum(1, 2, 3, 4, 5)
## [1] 15
median(1, 2, 3, 4, 5) #这会抛出错误
## Error: unused arguments (3, 4, 5)
在R中,不只是加号(+
),其他所有算术运算符都是向量化的。下例将演示减法、乘法、幂运算、两种除法及余数。
c(2, 3, 5, 7, 11, 13) - 2 # 减法
## [1] 0 1 3 5 9 11
-2:2 * -2:2 # 乘法
## [1] 4 1 0 1 4
identical(2 ^ 3, 2 ** 3) # 我们可用^或**代表求幂
# 尽管用^更加普遍
## [1] TRUE
1:10 / 3 # 浮点数除法
## [1] 0.3333 0.6667 1.0000 1.3333 1.6667 2.0000 2.3333 2.6667 3.0000 3.3333
1:10 %/% 3 # 整数除法
## [1] 0 0 1 1 1 2 2 2 3 3
1:10 %% 3 # 余数
## [1] 1 2 0 1 2 0 1 2 0 1
R还包含了多种数学函数。有三角函数(sin
、cos
、tan
,以及相反的asin
、acos
和atan
)、对数和指数(log
和exp
,以及它们的变种log1p
和expm1
,这两个函数对那些非常小的x
值能更加精确地计算log(1 + x)
和exp(x - 1)
的值),以及几乎所有其他你能想到的数学函数。请参考下例并加以理解。请再次注意,所有的函数都作用于向量,而不仅仅是单个值。
cos(c(0, pi / 4, pi / 2, pi)) #pi是内置常数
## [1] 1.000e+00 7.071e-01 6.123e-17 -1.000e+00
exp(pi * 1i) + 1 #欧拉公式
## [1] 0+1.225e-16i
factorial(7) + factorial(1) - 71 ^ 2 #5041是一个大数字
## [1] 0
choose(5, 0:5)
## [1] 1 5 10 10 5 1
要比较整数值是否相等请使用==
而不是单个等号=
,因为我们将会看到,单个等号另有用途。正如算术运算符一样,==
和其他关系运算符都是向量化的。要检查是否不相等,“不等”运算符为!=
。你可能已经猜到大于和小于号就是>
和<
(如果有可能相等,则使用>=
和<=
)。以下是几个例子:
c(3, 4 - 1, 1 + 1 + 1) == 3 # 操作符也是向量化的
## [1] TRUE TRUE TRUE
1:3 != 3:1
## [1] TRUE FALSE TRUE
exp(1:5) < 100
## [1] TRUE TRUE TRUE TRUE FALSE
(1:5) ^ 2 >= 16
## [1] FALSE FALSE FALSE TRUE TRUE
使用==
来比较非整型变量可能会带来问题。到目前为止。我们处理的所有数字都是浮点数。这意味着,对于两个数a
和b
来说,它们可存储为a * 2 ^ b
。由于它们都以32位存储,所以只能是一个近似值。这意味着舍入误差(rounding error)会常常潜伏在你的计算之中,你预期的答案可能是完全错误的。有很多书专门介绍这个主题,由于内容较多,这里就不过多介绍了。这是个常见的错误,R中的FAQ上有一个关于它的条目(http://bit.ly/17jZFfE),便于你深入了解。
看以下两个数字,它们应该是相同的。
sqrt(2) ^ 2 == 2 #sqrt是函数的平方根
## [1] FALSE
sqrt(2) ^ 2 - 2 #这个微小的差值就是舍入误差
## [1] 4.441e-16
R还提供了all.equal
函数用于检查数字是否相等。它提供了一个容忍度(tolerance level,默认情况下为1.5e-8
),因而那些小于此容忍度的舍入误差将被忽略:
all.equal(sqrt(2) ^ 2, 2)
## [1] TRUE
如果要比较的值不一样,all.equal
返回时将报告其差值。如果你需要它们在比较后返回的是一个TRUE
或FALSE
值,则应把all.equal
函数嵌入isTRUE
函数中调用:
all.equal(sqrt(2) ^ 2, 3)
## [1] "Mean relative difference: 0.5"
isTRUE(all.equal(sqrt(2) ^ 2, 3))
## [1] FALSE
小技巧
要检查两个数字是否一样,不要使用
==
,而使用all.equal
函数。
我们也可以使用==
来比较字符串。在这种情况下,比较会区分大小写,所以字符串必须完全匹配。理论上,也可以使用大于或小于(>
和<
)来比较字符串:
c(
"Can", "you", "can", "a", "can", "as",
"a", "canner", "can", "can", "a", "can?"
) == "can"
## [1] FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE TRUE FALSE ## [12] FALSE
c("A", "B", "C", "D") < "C"
## [1] TRUE TRUE FALSE FALSE
c("a", "b", "c", "d") < "C" # 你的结果可能有所不同
## [1] TRUE TRUE TRUE FALSE
然而在实际中,后一种方式总相当蹩脚,因为结果取决于你的语言环境(不同文化中充满了奇怪的字母排序规则,例如在爱沙尼亚语中,“z”排在“s”和“t”之间)。13.2节将讨论更强大的字符串匹配功能。
小技巧
在帮助页面中
?Arithmetic
、?Trig
、?Special
和?Comparison
有更多的例子,并提供边界情况下的大量处理细节。(如有兴趣,请试试0 ^ 0
或使用整数除以非整数。)