R_Advance

Advanced R学习笔记(四)Control flow

2019-03-09  本文已影响8人  ihtguy

介绍

有两种主要的控制结构,选择和循环.选择:像是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

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


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很有用,否则可以使用

上一篇 下一篇

猜你喜欢

热点阅读