consplan r语言_R语言笔记(七):条件与循环

在涉及大批量数据或重复操作时,使用流程控制能提高工作效率。流程控制主要内容包括:1. 条件执行结构1.1 if_else 语句

1.2 switch 语句

2. 循环执行结构2.1 for 和 while 语句

2.2 repeat 语句

2.3 循环中的 next 和 break

3. 循环结构在滚动回归分析中的运用

4. 附录

1. 条件执行结构

1.1 if_else 语句

基本语法:

if (condition) cons.expr else alt.expr

# 执行体代码量较少时可以在一行完成

if (condition) {

cons.expr_1

cons.expr_2

...

} else {

alt.expr_1

alt.expr_2

...

}

# 执行体代码量较多时可以用 {} 分行书写condition:判定条件,能够被转换为逻辑值 TRUE 或 FALSE

cons.expr:对应 TRUE 执行的命令

alt.expr:对应 FALSE 执行的命令

【注】若 condition 返回长度大于 1 的结果,就只会使用其中第一个元素进行判定

举几个例子:

x

if (x == 2) 1 else 2

# [1] 2

if (x > 2) 1 else 2

# [1] 1

x

if (x >= 0) sqrt(x) else 0

# [1] 1 0 NaN

if (x < 0) sqrt(x) else 0

# [1] 0

最后一个例子提示 warning,是因为 x 是向量 (1, 0, -1),x >= 0 返回结果是 (TRUE, TRUE, FALSE),系统对于长度大于 1 的 condition 只取第一个元素 TRUE,因此进行了开方运算;而 x < 0 返回结果是 (FALSE, FALSE, TRUE),取第一个元素 FALSE,因此返回 0。

要实现长度大于 1 的 condition 识别,一种方法是使用循环语句判定,另一种方法则是使用 ifelse() 函数,语法如下:

ifelse(test, yes, no)test:能被转换为逻辑型的对象

yes:对应 test 为 TRUE 执行的命令

no:对应 test 为 FALSE 执行的命令

用上面的例子:

x

ifelse(x >= 0, sqrt(x), NA)

# [1] 1 0 NA

看到这里就够了,下面的内容有点无聊,大家可以直接跳到 1.2 继续阅读 = =

为了弄清 ifelse() 的机制,作者又尝试了另外的例子:

x

yes

ifelse(x, yes, 0)

# [1] 5 6 7 0 5

ifelse 的返回似乎与 x 和 yes 的索引有关:x 转换为逻辑型变量后是 (T, T, T, F, T),test 为真对应的 x 的索引为 (1, 2, 3, NA, 5),将该索引应用到 yes 上,返回结果是 (yes[1], yes[2], yes[3], NA, yes[5]),其中 NA 使用 ifelse() 中 no 对应的 0 代替,而 yes[5] 则循环对应 yes[1],因此最终结果应该是(5, 6, 7, 0, 5),与返回的结果一致。ifelse() 具体的代码在附录查看,作者添加了注释以便大家阅读(实际可能造成阅读障碍,捂脸)。

1.2 switch 语句

基本语法:

switch(expr, object_1, object_2, ...)expr:长度为 1 的向量,数值型则根据索引值返回对象,字符型则根据对象名返回对象

若 expr 的返回值少于等于 object 总个数,则返回 expr 对应序数的 object

若 expr 的返回值多于 object 总个数,则返回 NULL

举几个例子:

a1

a2

a3

# 根据索引值返回对象

switch(1, a1, a2, a3)

# [1] 1 2 3

switch(3, a3, a2, a1)

# [1] 1 2 3

#根据对象名返回对象

switch("object2", object1 = a1, object2 = a2, object3 = a3)

# [,1] [,2] [,3]

# [1,] 1 5 9

# [2,] 2 6 10

# [3,] 3 7 11

# [4,] 4 8 12

switch 语句常与循环语句一起使用。

2. 循环执行结构

2.1 for 和 while 语句

基本语法:

for (var in seq) expr

# 循环体只有一条代码时

for (var in seq) {

expr_1

expr_2

...

}

# 循环体包含多条代码时var:一个变量

seq:一个序列,因子型数据会被转换为字符型

expr:循环体命令

while (cond) expr

while (cond) {

expr_1

expr_2

...

}cond:一个长度为 1 的非空逻辑型向量,长度大于 1 时仅使用第一个元素

for 和 while 使用较为简单,这里举几个例子:

for(i in 1: 3) print(1: i)

# [1] 1

# [1] 1 2

# [1] 1 2 3

set.seed(100)

for(n in c(2, 4, 8, 16, 32)) {

x

cat(n, ": ", sum(x^2), "\n", sep = "")

# 将 n、冒号、sum(x^2)拼接后返回

}

# 2: 0.2694976

# 4: 0.9078226

# 8: 2.26496

# 16: 10.87217

# 32: 39.83575

i = 1

while(i <= 3) {

print(1: i)

i = i + 1

}

# [1] 1

# [1] 1 2

# [1] 1 2 3

set.seed(100)

n = 2

while (n <= 32) {

x

cat(n, ": ", sum(x^2), "\n", sep = "")

n = n * 2

}

# 输出结果同 for 一致(记得运行 set.seed(100))

2.2 repeat 语句

基本语法:

repeat expr

repeat {

expr_1

expr_2

...

if (cond) break

}

repeat 会重复执行命令 expr,如果不使用 break 跳出循环,就会一直执行下去,相当于:

while (TRUE) expr

可以试试执行下列代码[doge]:

i = 0

repeat {

i = i + 1

print(i)

} # ESC 跳出循环

# 也可以写成 while

i = 0

while (T) {

i = i + 1

print(i)

}

2.3 循环中的 next 和 break

执行到 next ,就会跳过当前循环节剩余的代码,直接进入到下一个循环节中;执行到 break 则相反,会直接跳出整个循环。这两个语句通常和循环/条件执行结构一同使用。

举两个例子:

for (i in 1: 5) {

if (i == 3) next

# 当 i 等于 3 时,就跳过下列语句并进入下一个循环节

print(i)

}

# 输出结果为 1 2 4 5

i = 0

while (i <= 100) {

if (i > 10) break

# 当 i 大于 10 时,就终止循环

print(i)

i = i + 2

}

# 输出结果为 0 2 4 6 8 10

3. 循环结构在滚动回归分析中的运用

时间序列数据的分析中,回归分析是主要手段之一,可以通过对某一个时间段的数据回归得到该时间段对应的回归信息(系数、截距、残差等),但这只能获得一个时点的数据,如果我们想要获得这些回归信息随时间变化的情况,应该怎么办呢?

我们不妨将一个固定长度的时间段称为一个窗口(如一个月),以某个时间点作为研究的起始时间(如 2016 年 8 月 1 日),将第一个时间段作为第一个窗口(从 2016 年 8 月 1 日到 2016 年 9 月 1 日),对该窗口的数据进行回归拟合,获得第一组回归信息;而后将窗口向后一天移动,获得第二个窗口(从 2016 年 8 月 2 日到 2016 年 9 月 2 日),进行拟合得到第二组回归信息;以此类推。通过不断滚动窗口我们就能得到以天为单位,回归信息随时间变化的情况了,进而就能利用这个信息推断事物的发展规律或趋势。这种方法叫做“滚动回归分析法”(瞎编的= =我也不懂是不是叫这个)。

这种滚动回归就可以用循环执行结构实现,下面以医药板块从 2010-07-01 到 2018-06-29 的股市收益率为例分析(模型采用自回归条件异方差模型)。

library(timeSeries)

library(rugarch)

dat_link

dat

r

# 根据收盘价计算对数收益率

# 根据过去作者的拟合经验,选择三阶自回归模型

ar1 = c() # 储存一阶自回归系数

ar3 = c()

se1 = c() # 储存一阶自回归系数标准差

se3 = c()

# 设置模型参数

mod = ugarchspec(variance.model = list(model = "sGARCH",

garchOrder = c(1, 1),

submodel = NULL,

external.regressors = NULL,

variance.targeting = FALSE),

mean.model = list(armaOrder = c(3, 0),

include.mean = TRUE,

archm = FALSE,

archpow = 1,

arfima = FALSE,

external.regressors = NULL,

archex = FALSE),

distribution.model = "std")

# 数据量较小,跑完循环大概耗时两分钟

for(i in 1:243)

{

subr = r[i:(970+i)] # 设置窗口

cgarch = ugarchfit(mod, data = subr, solver = "solnp") # 拟合模型

coe = data.frame(cgarch@fit$coef)# 获取系数

std = data.frame(cgarch@fit$robust.se.coef) # 获取标准差

ar1[i] = coe[2, ] # 存储系数

ar3[i] = coe[4, ]

se1[i] = std[2, ] # 存储标准差

se3[i] = std[4, ]

}

ar1_up = ar1 + 1.96*se1 # 计算 ar1 95% 置信上界

ar1_low = ar1 - 1.96*se1 # 计算 ar1 95% 置信下界

ar3_up = ar3 + 1.96*se3

ar3_low = ar3 - 1.96*se3

result

ar1_up = ar1_up, ar1_low = ar1_low,

ar3_up = ar3_up, ar3_low = ar3_low)

获取了数据以后我们就可以绘图看看什么效果了:

先绘制一阶自相关系数的变化趋势图

library(ggplot2)

p

p + geom_line(aes(x = x, y = ar1)) +

geom_line(aes(x = x, y = ar1_up), color = I("blue")) +

geom_line(aes(x = x, y = ar1_low), color = I("blue")) +

geom_line(aes(x = x, y = 0),linetype = 2, color = I("red"))

黑色实现代表系数,两条蓝色实线分别是置信上界和下界,红色虚线落入两条蓝色实线之间则表明系数不显著。可以看到,一阶自相关系数围绕 0.05 上下波动,游离在显著边缘,说明收益率存在一阶自相关性,但其相关性较弱。

绘制三阶自回归系数变化趋势图

p + geom_line(aes(x = x, y = ar3)) +

geom_line(aes(x = x, y = ar3_up), color = I("blue")) +

geom_line(aes(x = x, y = ar3_low), color = I("blue")) +

geom_line(aes(x = x, y = 0),linetype = 2, color = I("red"))

三阶自相关系数有明显趋于 0 的趋势,0 刻度线最后落入置信区间,表明系数的显著性不断降低,三阶自相关性减弱。

基本思路就是使用 for/while/repeat 循环对每个窗口分别拟合数据,并将拟合结果整合到一起,从而实现滚动回归分析。

4. 附录

ifelse 的代码如下(函数的主要架构作者在注释中用【】分割):

function (test, yes, no)

{

#【test 是向量形式时执行以下代码】

if (is.atomic(test)) {

# is.atomic 判断 test 是否是向量

if (typeof(test) != "logical")

storage.mode(test)

# 将 test 转换为逻辑型数据

#【 test 长度为 1 时执行下列代码】

if (length(test) == 1 && is.null(attributes(test))) {

if (is.na(test))

return(NA)

# 如果 test 是 NA,直接返回 NA,否则执行下列代码

# test 为 TRUE 时执行下列代码

else if (test) {

if (length(yes) == 1) {

yat

if (is.null(yat) || (is.function(yes) && identical(names(yat),

"srcref")))

# 若 yes 符合上述格式,返回 yes,否则 report error

return(yes)

}

}

# test 为 TRUE 时执行上述代码

# test 为 FALSE 时执行下列代码

else if (length(no) == 1) {

nat

if (is.null(nat) || (is.function(no) && identical(names(nat),

"srcref")))

# 若 no 符合上述格式,返回 no,否则 report error

return(no)

}

# test 为 FALSE 时执行上述代码

}

}

#【 test 长度为 1 时执行上述代码】

#【test 是向量形式且长度为 1 时执行上述代码】

#【当 test 形式为递归或列表(非向量),或 test 长度大于 1 时,执行下列代码】

else test

methods::as(test, "logical")

else as.logical(test)

# 将 test 转换为逻辑型数据

ans

len

ypos

npos

if (length(ypos) > 0L) # 0L 指整数 0

ans[ypos]

# 将 test 为 TRUE 的索引应用到 yes 的循环上,并赋值给 ans

if (length(npos) > 0L)

ans[npos]

# 将 test 为 FALSE 的索引应用到 no 的循环上,并赋值给 ans

ans # 返回最终结果

#【当 test 形式为递归或列表(非向量),或 test 长度大于 1 时,执行上述代码】

}

可以目测下列代码执行结果作为练习:

x

y

z

ifelse(x, y, z)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值