for循环数据量太大_R语言笔记(七):条件与循环

eee9eb636934f1bc4ba89d99b16b6865.png

在涉及大批量数据或重复操作时,使用流程控制能提高工作效率。流程控制主要内容包括:

  • 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 <- 3
if (x == 2) 1 else 2
# [1] 2

if (x > 2) 1 else 2
# [1] 1

x <- c(1: -1)
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 <- c(1: -1)
ifelse(x >= 0, sqrt(x), NA)
# [1]  1  0 NA

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

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

x <- c(3: -1)
yes <- c(5: 8) # 5, 6, 7, 8
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 <- c(1, 2, 3)
a2 <- matrix(1:12, ncol = 3)
a3 <- data.frame(m = 1: 3, n = 4: 6)

# 根据索引值返回对象
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 <- stats::rnorm(n)
  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 <- stats::rnorm(n)
  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 <- "https://raw.githubusercontent.com/865547889/Logarithmic-rate-of-return/master/SH%23880489.txt"
dat <- read.table(dat_link, header = T)

r <- diff(log(dat[, 5]), 1)*100
# 根据收盘价计算对数收益率

# 根据过去作者的拟合经验,选择三阶自回归模型
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 <- data.frame(x = 1:243, ar1 = ar1, ar3 = ar3,
                     ar1_up = ar1_up, ar1_low = ar1_low,
                     ar3_up = ar3_up, ar3_low = ar3_low)

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

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

library(ggplot2)
p <- ggplot(data = result)
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"))

d6712a3bdec8493a5bb00064a527c328.png

黑色实现代表系数,两条蓝色实线分别是置信上界和下界,红色虚线落入两条蓝色实线之间则表明系数不显著。可以看到,一阶自相关系数围绕 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"))

acaf82d64786920b10ddc9e07ef7a94f.png

三阶自相关系数有明显趋于 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) <- "logical"
      # 将 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 <- attributes(yes)
                  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 <- attributes(no)
                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 <- if (isS4(test)) 
        methods::as(test, "logical")
    else as.logical(test)
  # 将 test 转换为逻辑型数据

    ans <- test
    len <- length(ans)
    ypos <- which(test) # 返回 test 为 TRUE 的索引
    npos <- which(!test)# 返回 test 为 FALSE 的索引

    if (length(ypos) > 0L) # 0L 指整数 0
        ans[ypos] <- rep(yes, length.out = len)[ypos] # rep 确保 yes “够用”
        # 将 test 为 TRUE 的索引应用到 yes 的循环上,并赋值给 ans

    if (length(npos) > 0L) 
        ans[npos] <- rep(no, length.out = len)[npos]
        # 将 test 为 FALSE 的索引应用到 no 的循环上,并赋值给 ans

    ans # 返回最终结果
  #【当 test 形式为递归或列表(非向量),或 test 长度大于 1 时,执行上述代码】
}

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

x <- c(3: -1) # 3 2 1 0 -1
y <- c("I", "love", "rlanguage", 520, "badminton")
z <- c("him", "you", 1314, "and", "her")
ifelse(x, y, z)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值