R 提供的运算符
R 的运算符分为四组:算术运算符(+ - * / ^ %% %/%)、比较运算符(== != < <= > >=)、逻辑运算符(& | && || ! xor),以及成员检测运算符 %in%。有两点让它们和大多数语言不同:几乎所有运算符都是向量化的,会一次性对整个向量逐元素运算;而且 R 有两个与运算符和两个或运算符,它们不能互换。本页会讲解所有这些运算符,以及会悄悄给出错误答案的优先级陷阱。
算术运算符:+、-、*、/、^、%% 和 %/%
前五个的作用和你预想的一样。用百分号包起来的两个,%%(取模,即余数)和 %/%(整数除法,即商),才是大家常去搜索的:
注意 / 总是做浮点除法:17 / 5 是 3.4,永远不会是 3。如果你想要整数部分,%/% 正是为此而生的。
%% 的日常用途是判断能否整除。一个数除以 2 没有余数时,它就是偶数:
第二行值得停下来看一看:当操作数为负数时,R 的 %% 返回的结果与除数的符号相同(-7 %% 3 是 2,因为 -7 = -3 * 3 + 2)。有些语言的做法正好相反;如果你在移植代码,记得检查一下。
比较运算符是向量化的
==、!=、<、<=、>、>= 会逐元素比较,返回一个逻辑向量,而不是单个答案:
这几乎是 R 中所有筛选操作背后的引擎:你把整个向量和一个值比较,得到一个逻辑掩码,再用这个掩码取子集。对小数使用 == 时要小心:浮点数是近似存储的,所以 0.1 + 0.2 == 0.3 是 FALSE。比较小数时,优先使用 all.equal(),或者检查 abs(x - y) < 1e-9。
逻辑运算符:& 与 &&(经典的混淆)
R 有两个与运算符和两个或运算符,选错是每个人的必经之路。
& 和 | 是向量化的。 它们逐元素组合两个逻辑向量:
只要是在为数据构造筛选条件,就用它们:df[df$age > 30 & df$city == "Lisbon", ]。
&& 和 || 是标量运算符,并且会短路求值。 它们接收单个值,返回单个 TRUE/FALSE;如果第一个操作数已经决定了答案,就完全跳过第二个操作数:
第二行演示了短路求值:因为 x < 0 是 FALSE,R 根本不会求值 stop() 调用。这正是 && 适合放在 if() 条件中的原因:你可以写 if (!is.na(x) && x > 10),当 x 是 NA 时,x > 10 这部分会被安全地跳过。
经验法则:向量和数据筛选用 &/|,if() 条件用 &&/||。 而且从 R 4.3 开始,这条规则有了强制力:给 && 或 || 传入长度大于 1 的向量会报错(旧版本的 R 会悄悄只使用第一个元素,这让真正的 bug 隐藏了好多年)。
xor(a, b) 补全了这一组:当两者中恰好一个为 TRUE 时返回 TRUE:
%in%:成员检测
x %in% table 会针对 x 的每个元素询问:“它是否出现在 table 中?”它是实际 R 代码中最常用的运算符之一:
被检查的一侧可以是整个向量,这让 %in% 成为按一组允许值筛选行的天然工具。
它也胜过你原本会写的替代方案,也就是用 | 连起来的一串 ==:
如果有五个允许的值,== 链就会变成一堵重复的墙;%in% 版本只需要把向量加长。它还有第二个更微妙的优势:%in% 永远不会返回 NA:
NA == 1 是 NA,因为 R 无法知道一个未知值是否等于 1,这一行为在缺失值中有深入讲解。这个 NA 接着会毒害它所到达的任何 if()。%in% 总是回答 TRUE 或 FALSE,从而完全绕开了这个问题,所以对可能有缺失值的数据设置条件时,它是更安全的选择。
优先级陷阱
在“R 坏掉了”的时刻中,有两个运算符优先级陷阱占了不成比例的份额。第一,^ 的优先级高于一元负号:
第二,冒号的优先级高于二元算术运算:
拿不准时就加括号。括号不花任何成本,而且每个读者(包括将来的你)都会和解释器以同样的方式解析它们。
$ 与 [ ]:访问运算符
你还会经常看到 $、[ ] 和 [[ ]],严格来说它们也是运算符,只不过是用来从数据结构中取值,而不是用来计算的:
关于向量、列表和数据框的页面会对它们做完整的讲解,在这里只需要认出它们也属于同一个运算符家族就够了。
要点回顾
%%给出余数,%/%给出整数商;x %% 2 == 0用来判断偶数。- 比较运算是向量化的:它们返回一个可以用来筛选的逻辑向量。
&/|逐元素组合向量;&&/||用于if()中的单个值,并且会短路求值。从 R 4.3 开始,对更长的向量使用&&会报错。x %in% table可以替代串联的==,而且永远不会返回NA。-2^2是-4,1:n - 1从 0 开始:拿不准时就加括号。
下一篇:在 if、else 和 ifelse() 中运用这些运算符,也就是 R 的条件语句。
常见问题
R 语言中 %% 是什么意思?
%% 是取模运算符,返回除法的余数。17 %% 5 等于 2。与它配套的 %/% 做整数除法,返回不带余数的商:17 %/% 5 等于 3。%% 的经典用法是判断偶数:x %% 2 == 0。
R 语言中 & 和 && 有什么区别?
& 是向量化的:它逐元素比较两个向量,返回一个长度相同的向量。&& 只作用于单个值,会短路求值(如果第一个操作数已经决定了结果,就跳过第二个),并且恰好返回一个 TRUE 或 FALSE。筛选向量时用 &,在 if() 条件中用 &&。从 R 4.3 开始,给 && 或 || 传入长度大于 1 的向量会直接报错,而不是警告。
R 语言中 %in% 有什么作用?
x %in% table 检查 x 的每个元素是否出现在 table 中,并为每个元素返回 TRUE 或 FALSE。"mango" %in% basket 可以替代用 | 连接起来的一串 == 比较;而且和 == 不同,它永远不会返回 NA:被检查一侧的缺失值只会得到 FALSE。
R 语言中 -2^2 为什么返回 -4?
因为 ^ 的优先级高于一元负号,R 会把 -2^2 读作 -(2^2),也就是 -4。如果你想要的是负二的平方,请写 (-2)^2,结果是 4。冒号也有类似的陷阱:1:n - 1 的意思是 (1:n) - 1,而不是 1:(n - 1)。