美文网首页R_Advance
Advanced R学习笔记(四)Control flow

Advanced R学习笔记(四)Control flow

作者: ihtguy | 来源:发表于2019-03-09 17:09 被阅读8次

    介绍

    有两种主要的控制结构,选择和循环.选择:像是if和switch().允许根据不同的输入执行不同的代码.循环: 像是for和while,允许重复执行代码,通常使用变化的选项.

    选择

    在R中是这样子的

    
    if (condition) true_action
    
    if (condition) true_action else false_action
    
    

    通常操作是被{包括

    
    grade <- function(x) {
    
      if (x > 90) {
    
        "A"
    
      } else if (x > 80) {
    
        "B"
    
      } else if (x > 50) {
    
        "C"
    
      } else {
    
        "F"
    
      }
    
    }
    
    

    if返回一个值以便于分配这个结果

    
    x1 <- if (TRUE) 1 else 2
    
    x2 <- if (FALSE) 1 else 2
    
    c(x1, x2)
    
    #> [1] 1 2
    
    

    建议当if只有一行时才进行赋值,否则会很难读懂

    当不使用else的时候,if将会在条件是错的时候返回NULL,c()和paste()会略去NULL的输入,使得更加简洁.

    
    greet <- function(name, birthday = FALSE) {
    
      paste0(
    
        "Hi ", name,
    
        if (birthday) " and HAPPY BIRTHDAY"
    
      )
    
    }
    
    greet("Maria", FALSE)
    
    #> [1] "Hi Maria"
    
    greet("Jaime", TRUE)
    
    #> [1] "Hi Jaime and HAPPY BIRTHDAY"
    
    

    无效的输入

    条件应是单独的TRUE或者FALSE,大部分其他的输入将会产生一个错误.

    
    if ("x") 1
    
    #> Error in if ("x") 1:
    
    #> argument is not interpretable as logical
    
    if (logical()) 1
    
    #> Error in if (logical()) 1:
    
    #> argument is of length zero
    
    if (NA) 1
    
    #> Error in if (NA) 1:
    
    #> missing value where TRUE/FALSE needed
    
    if ("x") 1
    
    #> Error in if ("x") 1:
    
    #> argument is not interpretable as logical
    
    if (logical()) 1
    
    #> Error in if (logical()) 1:
    
    #> argument is of length zero
    
    if (NA) 1
    
    #> Error in if (NA) 1:
    
    #> missing value where TRUE/FALSE needed
    
    

    当一个长度大于一的逻辑向量则只会产生一个警告

    
    if (c(TRUE, FALSE)) 1
    
    #> Warning in if (c(TRUE, FALSE)) 1: the condition has length > 1 and only the
    
    #> first element will be used
    
    #> [1] 1
    
    

    在R3.5之后可以通过设置环境变量使得这返回一个错误

    
    Sys.setenv("_R_CHECK_LENGTH_1_CONDITION_" = "true")
    
    if (c(TRUE, FALSE)) 1
    
    #> Error in if (c(TRUE, FALSE)) 1:
    
    #> the condition has length > 1
    
    

    Vectorised if

    当想操纵长度不为一的向量时,可以使用ifelse(),给出的参数是测试,是的情况下的操作和错误情况下的操作

    
    x <- 1:10
    
    ifelse(x %% 5 == 0, "XXX", as.character(x))
    
    #> [1] "1" "2" "3" "4" "XXX" "6" "7" "8" "9" "XXX"
    
    ifelse(x %% 2 == 0, "even", "odd")
    
    #> [1] "odd" "even" "odd" "even" "odd" "even" "odd" "even" "odd" "even"
    
    

    缺失值将会被传播到输出

    建议在输出时两种输出同种类型时才使用ifelse(),否则很难预料输出什么类型的对象.

    其他的向量化的if可以用dplyr::case_when()

    
    dplyr::case_when(
    
      x %% 35 == 0 ~ "fizz buzz",
    
      x %% 5 == 0 ~ "fizz",
    
      x %% 7 == 0 ~ "buzz",
    
      is.na(x) ~ "???",
    
      TRUE ~ as.character(x)
    
    )
    
    #> [1] "1" "2" "3" "4" "fizz" "6" "buzz" "8" "9" "fizz"
    
    

    switch()语句

    更像if 是一个紧凑的版本.

    
    x_option <- function(x) {
    
      if (x == "a") {
    
        "option 1"
    
      } else if (x == "b") {
    
        "option 2" 
    
      } else if (x == "c") {
    
        "option 3"
    
      } else {
    
        stop("Invalid `x` value")
    
      }
    
    }
    
    

    可以这么写

    
    x_option <- function(x) {
    
      switch(x,
    
        a = "option 1",
    
        b = "option 2",
    
        c = "option 3",
    
        stop("Invalid `x` value")
    
      )
    
    }
    
    

    最后一行应该给出一个错误,否则就会给出不可见的NULL

    
    (switch("c", a = 1, b = 2))
    
    #> NULL
    
    

    如果多个输入有同一个输出,可以将等号右边空置,它将会滑向下一个值

    
    legs <- function(x) {
    
      switch(x,
    
        cow = ,
    
        horse = ,
    
        dog = 4,
    
        human = ,
    
        chicken = 2,
    
        plant = 0,
    
        stop("Unknown input")
    
      )
    
    }
    
    legs("cow")
    
    #> [1] 4
    
    legs("dog")
    
    #> [1] 4
    
    

    但是只建议对于字符输入使用switch(),对于数字会很难阅读,而且可能在非整数时出现一些未知的错误

    循环

    for循环用于遍历向量中的每一项.基础形式如下

    
    for (item in vector) perform_action
    
    
    
    for (i in 1:3) {
    
      print(i)
    
    }
    
    #> [1] 1
    
    #> [1] 2
    
    #> [1] 3
    
    

    但是要注意for会把item赋值给当前环境,覆盖已经存在的同一个名字的变量

    
    i <- 100
    
    for (i in 1:3) {}
    
    i
    
    #> [1] 3
    
    

    有两种方法去提前结束循环

    • next 退出当前迭代

    • break 退出整个循环

    
    for (i in 1:10) {
    
      if (i < 3) 
    
        next
    
      print(i)
    
    
    
      if (i >= 5)
    
        break
    
    }
    
    #> [1] 3
    
    #> [1] 4
    
    #> [1] 5
    
    

    常见的错误

    首先,如果已经产生了一个树精,应该预先产生一个容纳它的对象,否则循环会非常慢

    
    means <- c(1, 50, 20)
    
    out <- vector("list", length(means))
    
    for (i in 1:length(means)) {
    
      out[[i]] <- rnorm(10, means[[i]])
    
    }
    
    

    其次,要小心1:length(x).如果有x长度为0时

    
    means <- c()
    
    out <- vector("list", length(means))
    
    for (i in 1:length(means)) {
    
      out[[i]] <- rnorm(10, means[[i]])
    
    }
    
    #> Error in rnorm(10, means[[i]]):
    
    #> invalid arguments
    
    

    这是因为:同时适用于递增和递减的序列,可以使用seq_along()

    
    seq_along(means)
    
    #> integer(0)
    
    out <- vector("list", length(means))
    
    for (i in seq_along(means)) {
    
      out[[i]] <- rnorm(10, means[[i]])
    
    }
    
    

    最后,在循环S3类的时候可能会遇到问题,因为循环会去掉属性

    
    xs <- as.Date(c("2020-01-01", "2010-01-01"))
    
    for (x in xs) {
    
      print(x)
    
    }
    
    #> [1] 18262
    
    #> [1] 14610
    
    

    可以通过调用自己来解决

    
    for (i in seq_along(xs)) {
    
      print(xs[[i]])
    
    }
    
    #> [1] "2020-01-01"
    
    #> [1] "2010-01-01"
    
    

    相关的工具

    如果提前知道要迭代的数据,那么for很有用,否则可以使用

    • while(condition) action 执行action 当condition是TRUE时

    • repeat(action) 重复action永远

    相关文章

      网友评论

        本文标题:Advanced R学习笔记(四)Control flow

        本文链接:https://www.haomeiwen.com/subject/unqepqtx.html