任性的盒饭 · 编译错误:语句无效_编译错误:缺少retur ...· 4 天前 · |
害羞的篮球 · RStudio 断点调试 ...· 5 天前 · |
直爽的青蛙 · (7)javascript的程序控制结构及语 ...· 6 天前 · |
冷静的茶壶 · js中怎么跳出循环不执行后面代码 | ...· 6 天前 · |
爱看球的杨桃 · Kurt Hatlevik – ...· 3 月前 · |
千年单身的闹钟 · 豫教要闻 - 河南省教育厅· 4 月前 · |
考研的台灯 · 小米突围战:两年投资12家供应链企业,雷军还 ...· 5 月前 · |
不羁的稀饭 · Nexus构建npm、yum、maven私有 ...· 5 月前 · |
犯傻的小笼包 · crontab每周一执行一次-掘金· 1 年前 · |
evalCpp()
转换单一计算表达式
cppFunction()
转换简单的C++函数—Fibnacci例子
sourceCpp()
转换C++程序—正负交替迭代例子
sourceCpp()
转换C++源文件中的程序—正负交替迭代例子
sourceCpp()
转换C++源程序文件—卷积例子
wrap()
把C++变量返回到R中
as()
函数把R变量转换为C++类型
as()
和
wrap()
的隐含调用
//[[Rcpp::export]]
sourceCpp()
函数中直接包含C++源程序字符串
cppFunction()
函数中直接包含C++函数源程序字符串
evalCpp()
函数中直接包含C++源程序表达式字符串
depends
指定要链接的库
invisible
要求函数结果不自动显示
clone
函数
is_na
seq_along
seq_len
pmin
和
pmax
ifelse
sapply
和
lapply
sign
diff
kable()
函数制作表格
## [1] 0 1 1
函数
ifelse(test, yes, no)
中的
test
是逻辑向量,
yes
和
no
是向量,
test
、
yes
和
no
的配合符合向量化原则,
如果有长度为1的或者长度较短但其倍数等于最长一个的长度的,
短的一个自动从头循环使用。如:
## [1] -1 -2 1 2 1 2
当然,最常见的还是
yes
、
no
为标量的情形。
不同于
if
语句,
ifelse
的
test
中运行有缺失值,
对应结果也是缺失值。
dplyr包的
if_else
函数与基本R的
ifelse
功能基本相同,
但允许指定条件为缺失值时的不同处理方法。
switch
函数
函数
switch()
可以建立多分枝结构,
可以根据一个整数表达式或者一个字符串表达式的值选择返还相对应的值。
例如,某校将百分制转换到等级制的规则如下:
从百分制转换为等级制可以用
if-else if-else
完成。
从等级制转换回百分制,用区间平均值,可以写成
switch()
的写法:
gr_v <- c("A+", "A", "A-", "B+", "B", "B-", "C+", "C", "C-", "D+", "F", "EX") for(gr in gr_v){ switch(gr, "A+" = 99, "A" = 96, "A-" = 92, "B+" = 87, "B" = 82, "B-" = 78, "C+" = 75, "C" = 71, "C-" = 68, "D+" = 65, "F" = 50,
switch()
中的最后一个选择通常用于所有前面选择都不匹配时的一个例外情况处理, 这里的例子对这种情况返回缺失值。 上例中用了for
循环,见下一节的说明。
switch()
中允许对多个值采取同一行动或者返回同一结果,for(ch in c("a", "i", "c")){ cat(ch, ":", switch(ch, "a"=, "e"=, "i"=, "o"=, "u" = "Vower", "Consonant"), "\n")
## a : Vower ## i : Vower ## c : Consonant
在上例中,a, e, i, o, u返回相同的值
"Vower"
, 而其它值则返回"Consonant"
。如果要建立\(1:n\)到\(x_1 \sim x_n\)的映射, 可以用整数下标的方法; 如果要建立\(s_1 \sim s_n\)(字符串向量)到\(x_1 \sim x_n\)的映射, 可以用有名向量的方法。
switch()
则可以处理有例外项的问题。 更一般的分支, 尤其是不同分支执行不同命令的情况, 应该使用if-else if-else
结构。dplyr包的
case_when
函数可以switch()
的向量化推广, 看成是ifelse
的多分支推广, 或看成if-else if-else语句的向量化。 输入条件是向量化的, 根据输入条件返回条件对应的结果。 所有条件都不满足的元素的输出结果为
NA`。v <- c("A+", "A", "A-", "B+", "B", "B-", "C+", "C", "C-", "D+", "F", "EX") dplyr::case_when( v == "A+" ~ 99, v == "A" ~ 96, v == "A-" ~ 92, v == "B+" ~ 87, v == "B" ~ 82, v == "B-" ~ 78, v == "C+" ~ 75, v == "C" ~ 71, v == "C-" ~ 68, v == "D+" ~ 65, v == "F" ~ 50)
## [1] 99 96 92 87 82 78 75 71 68 65 50 NA
x <- c("a", "i", "c") dplyr::case_when( x %in% c("a", "e", "i", "o", "u") ~ "Vowel", ~ "Consonant"
## [1] "Vowel" "Vowel" "Consonant"
16.3 循环结构
16.3.1 计数循环
为了对向量每个元素、矩阵每行、矩阵每列循环处理,语法为
for(循环变量 in 序列) 语句
其中的语句一般是复合语句。
set.seed(101); x <- rnorm(5) y <- numeric(length(x)) for(i in 1:5){ if(x[i]>=0) y[i] <- 1 else y[i] <- 0 print(y)
## [1] 0 1 0 1 1
其中
rnorm(5)
会生成5个标准正态分布随机数。numeric(n)
生成有n个0的数值型向量(基础类型为double)。如果需要对某个向量
x
按照下标循环, 获得所有下标序列的标准写法是seq_along(x)
, 而不用1:n
的写法, 因为在特殊情况下n
可能等于零,这会导致错误下标, 而seq_along(x)
在x
长度为零时返回零长度的下标。例如,设序列\(x_n\)满足\(x_0=0\), \(x_n = 2 x_{n-1} + 1\), 求\(S_n = \sum_{i=1}^n x_n\):
## [1] 57
在R中应尽量避免for循环: 其速度比向量化版本慢一个数量级以上, 而且写出的程序不够典雅。 比如,前面那个示性函数例子实际上可以简单地写成
## [1] 0 1 0 1 1
许多计数循环都可以用
lapply
、vapply
、sapply
、apply
、map
、Map
等函数替代, 详见18.3。用
break
语句退出所在的循环。 用next
语句进入所在循环的下一轮。使用for循环的注意事项:
如果对向量每个元素遍历并保存结果, 应在循环之前先将结果变量产生等长的存储, 在循环内为已经分配好存储空间的输出向量的元素赋值。 为了产生长度为 n
的数值型向量,用numeric(n)
; 为了产生长度为n
的列表,用vector("list", n)
。对一个向量元素遍历时如果用下标访问, 需要用 seq_along(x)
的做法而不是1:length(x)
的做法。如果直接对向量元素遍历, 这有可能会丢失向量的属性(如日期型), 用下标访问则不存在此问题。 ## [1] 360086400 ## [1] 1582300800
## [1] "1981-05-31 CST" ## [1] "2020-02-22 CST"
16.3.2
while
循环和repeat
循环while(循环继续条件) 语句
进行当型循环。 其中的语句一般是复合语句。 仅当条件成立时才继续循环, 而且如果第一次条件就已经不成立就一次也不执行循环内的语句。
repeat 语句
进行无条件循环(一般在循环体内用if与break退出)。 其中的语句一般是复合语句。 如下的写法可以制作一个直到型循环:
repeat{ if(循环退出条件) break
直到型循环至少执行一次, 每次先执行
常量\(e\)的值可以用泰勒展开式表示为 e = 1 + \sum_{k=1}^\infty \frac{1}{k!} R函数...
代表的循环体语句, 然后判断是否满足循环退出条件, 满足条件就退出循环。exp(1)
可以计算e
的为了计算\(e\)的值, 下面用泰勒展开逼近计算e
的值:e0 <- exp(1.0) s <- 1.0 x <- 1 k <- 0 repeat{ k <- k+1 x <- x/k s <- s + x if(x < .Machine$double.eps) break err <- s - e0 cat("k=", k, " s=", s, " e=", e0, " 误差=", err, "\n")
## k= 18 s= 2.718282 e= 2.718282 误差= 4.440892e-16
其中
.Machine$double.eps
称为机器\(\varepsilon\), 是最小的加1之后可以使得结果大于1的正双精度数, 小于此数的正双精度数加1结果还等于1。 用泰勒展开公式计算的结果与exp(1)
得到的结果误差在\(10^{-16}\)左右。16.4 R中判断条件
if语句和while语句中用到条件。 条件必须是标量值, 而且必须为TRUE或FALSE, 不能为NA或零长度。 这是R编程时比较容易出错的地方。
16.5 管道控制
数据处理中经常会对同一个变量(特别是数据框)进行多个步骤的操作, 比如,先筛选部分有用的变量,再定义若干新变量,再排序。 R从4.1.0版本开始提供了一个
|>
运算符实现这样的操作流程, R的magrittr包提供了一个%>%
运算符执行类似功能。 比如,变量x
先用函数f(x)
进行变换,再用函数g(x)
进行变换, 一般应该写成g(f(x))
,用|>
运算符,可以表示成x |> f() |> g()
。 更多的处理,如h(g(f(x)))
可以写成x |> f() |> g() |> h()
。 这样的表达更符合处理发生的次序,而且插入一个处理步骤也很容易。处理用的函数也可以带有其它自变量,在管道控制中不要写第一个自变量。
## [1] 4.11325
结果为\(e^{\sqrt{2}}\)。
## [1] 0.5236
这等价于
round(asin(0.5), digits=4)
。在
|>
表示的管道中, 上一步的输出通过管道作为下一步的第一个自变量。 如果需要当作下一步的其它自变量, 可以用自变量名 = _
的特殊格式, 这里_
代表管道中上一步的结果。如:## [1] 3.141593
这等价于
round(pi, digits=6)
。tibble类型的数据框尤其适用于如此的管道操作, 在23中有大量使用管道进行多步骤操作的例子。
magrittr包定义了
%T%
运算符,x %T% f()
返回x
本身而不是用f()
修改后的返回值f(x)
, 这在中间步骤需要显示或者绘图但是需要进一步对输入数据进行处理时有用。magrittr包定义了
%$%
运算符, 此运算符的作用是将左运算元的各个变量(这时左运算元是数据框或列表)暴露出来, 可以直接在右边调用其中的变量,类似于with()
函数的作用。magrittr包定义了
%<>%
运算符, 用在管道链的第一个连接, 可以将处理结果存入最开始的变量中, 类似于C语言的+=
运算符。如果一个操作是给变量加
b
,可以写成add(b)
, 给变量乘b
,可以写成multiply_by(b)
。