“条件的长度 > 1,并且只有第一个元素将被使用”来自数据框上嵌套的“if elses”的警告

“the condition has length > 1 and only the first element will be used” warning from nested `if elses' over a dataframe

我有一个包含两列的数据框,Q10_headache_tibble:

structure(list(df_questionaire.headaches = c(0L, 2L, 2L, 2L, 
0L, 0L, 0L, 0L, 2L, 0L, 2L, 2L, 0L, 2L, 0L, 2L, 2L, 2L, 2L, 2L, 
2L, 0L, 2L, 0L, 2L, 0L, 2L, NA, 2L, 2L, 0L, 2L, 0L, 2L, 2L, 0L, 
0L, 0L, 0L, 2L, 0L, 0L, 0L, 0L, 0L, 2L, 2L, 0L, 0L, 0L, 0L, 0L, 
0L, 2L, 0L, 2L, 2L, 0L, 0L, 2L, 0L, 0L, 0L, 0L, 2L, 0L, 2L, 0L, 
0L, 0L, 2L, 0L, 2L, 0L, 2L, 0L, 0L, 2L, 2L, 0L, 0L, 2L, 2L, 2L, 
0L, 0L, 0L, 0L, 2L, 0L, 2L, 0L, 0L, 0L, 0L, 2L, 0L, 2L, 2L, 2L, 
2L, 0L, 0L, 0L, 0L, 2L, 0L, 2L, 2L, 0L, 0L, 2L, 0L, 0L, 0L, 2L, 
0L, 2L, 2L, 0L, 0L, 2L, 0L, 2L, 2L, 0L, 2L, 2L, 2L, 2L, 0L, 0L, 
0L, 0L, 2L, 0L, 0L, 0L, 2L, 0L, 0L, 0L, 0L, 0L, 2L, 0L, 0L, 2L, 
2L, 0L, 0L, 0L, 2L, 0L, 0L, 0L, 0L, 0L, 2L, 2L, 0L, 2L, 0L, 0L, 
0L, 0L, 2L, 2L, 2L, 2L, 2L, 0L, 2L, 0L, 0L), df_questionaire.headaches_covid = c(0L, 
0L, 2L, 2L, 2L, 0L, 0L, 0L, 0L, 2L, 0L, 2L, 0L, 0L, 0L, 0L, 2L, 
2L, 2L, 2L, 2L, 0L, 2L, 0L, 2L, 2L, 0L, NA, 2L, 2L, 0L, 0L, 0L, 
2L, 2L, 0L, 0L, 0L, 0L, 2L, 0L, 0L, 0L, 0L, 2L, 2L, 0L, 0L, 0L, 
0L, 2L, 0L, 0L, 2L, 0L, 2L, 0L, 0L, 2L, 0L, 0L, 0L, 0L, 0L, 2L, 
0L, 0L, 774L, 0L, 0L, 0L, 2L, 2L, 774L, 0L, 0L, 0L, 2L, 0L, 2L, 
0L, 2L, 0L, 2L, 0L, 0L, 2L, 0L, 2L, 0L, 2L, 0L, 0L, 0L, 0L, 0L, 
0L, 2L, 2L, 0L, 2L, 0L, 2L, 2L, 0L, 2L, 0L, 0L, 2L, 0L, 0L, 2L, 
2L, 2L, 0L, 2L, 0L, 2L, 0L, 0L, 2L, 2L, 0L, 2L, 0L, 0L, 0L, 2L, 
2L, 0L, 0L, 0L, 0L, 0L, 2L, 2L, 0L, 0L, 2L, 0L, 0L, 0L, 0L, 0L, 
2L, 0L, 0L, 2L, 2L, 0L, 774L, 0L, 2L, 0L, 0L, 0L, 0L, 0L, 0L, 
2L, 0L, 2L, 774L, 0L, 2L, 0L, 0L, 2L, 2L, 2L, 2L, 774L, 0L, 0L, 
774L)), row.names = c(NA, -175L), class = c("tbl_df", "tbl", 
"data.frame"))

我创建了一个函数,它应该 return 一个字符向量 (Q10_incidence) 与 nrow(df_headache_tibble) 的长度相同,基于应用于数据框的嵌套条件,按行。 Q10_incidence[i] 应该是将函数应用于 df_headache_tibble[i,1] 和 df_headache_tibble[i,2] 的结果,我打算为此使用 mapply。

incidence_headaches<-function(x,y){
        if (is.na(x)|is.na(y)){
                        output<-NA
                }
        else if (x==2){
                if (y==2){
                        output<-'previous_headache_maintained'
                }else if(y==0){
                        output<-'previous_headache_ceased'
                }
        }else if(x %in% c(0,774,775,776)){
                if (y==2){
                        output<-'new_onset_headache'
                }else if (y %in% c(0, 774, 775, 776)){
                        output<-'no_headache'
                }
        }
}

Q10_incidence<-mapply(incidence_headaches, Q10_headache_tibble[,1], Q10_headache_tibble[,2])

当我打电话时

mapply(incidence_headaches, Q10_headache_tibble[,1], Q10_headache_tibble[,2])

我在几个警告中收到可怕的“条件的长度 > 1,并且只会使用第一个元素”。我怎么能处理这个? 虽然我发现了几个关于同一个“condition has length (...)”警告的问题,但我仍然觉得这个话题很混乱。欢迎“傻瓜式”演练。

好像和向量化有关系,把函数换成嵌套的ifelse()结构就可以解决,会很乱

我可能需要在很多场合使用类似的功能,不确定什么是最好的解决方法。

1) 就我个人而言,我尽可能多地使用 R,只使用其众多命令中的一小部分。也许一个简单的 apply 是一种更简单的管理方法。 applyMARGIN = 1 会将 data.frame 的每一行赋予一个函数。所以我对你的函数做了这个小改动(这里只对前 3 行感兴趣,其余的是复制和粘贴):

incidence_headaches<-function(row){
  x <- row[1]
  y <- row[2]
  if (is.na(x)|is.na(y)){
    output<-NA
  }
  else if (x==2){
    if (y==2){
      output<-'previous_headache_maintained'
    }else if(y==0){
      output<-'previous_headache_ceased'
    }
  }else if(x %in% c(0,774,775,776)){
    if (y==2){
      output<-'new_onset_headache'
    }else if (y %in% c(0, 774, 775, 776)){
      output<-'no_headache'
    }
  }
}

然后您可以像这样使用简单的 apply

apply(df_headache_tibble, MARGIN = 1, incidence_headaches)

要得到这样的东西:

> apply(df_headache_tibble, MARGIN = 1, incidence_headaches)
  [1] "no_headache"                  "previous_headache_ceased"     "previous_headache_maintained"
  [4] "previous_headache_maintained" "new_onset_headache"           "no_headache"                 
  [7] "no_headache"                  "no_headache"                  "previous_headache_ceased"    
 [10] "new_onset_headache"           "previous_headache_ceased"     "previous_headache_maintained"
 [13] "no_headache"                  "previous_headache_ceased"     "no_headache" 
...

2) mapply 显然是一个完美的函数,没有理由不使用它。你的问题是:tibbles 是 data.frames 但它们的行为不像 data.frames。这很好用:

mapply(incidence_headaches, 
       as.data.frame(df_headache_tibble)[,1],
       as.data.frame(df_headache_tibble)[,2])

当您仅从 data.frame 中提取一行时,它将为您提供一个向量,当您仅从小标题中提取一行时,它将为您提供一个小标题。 Hadley 与发明 R data.frame 的人对事情应该如何运作有不同的看法。

中有一些解决方法
mapply(incidence_headaches, 
       df_headache_tibble[,1, drop = TRUE],
       df_headache_tibble[,2, drop = TRUE])

在此处阅读详细信息,但最重要的是要始终注意,虽然小标题是 data.frames,但它们的行为并不完全像 data.frames:https://tibble.tidyverse.org/reference/subsetting.html

这是一个完全矢量化的解决方案,不需要 *apply 循环。

incidence_headaches <- function(x, y){
  # create the return vector
  output <- rep('no_headache', NROW(x))
  # conditions for 'x'
  x_2 <- x == 2
  x_vec <- x %in% c(0, 774, 775, 776)
  # conditions for 'y'
  y_2 <- y == 2
  y_vec <- y %in% c(0, 774, 775, 776)
  # assign the return values given a combination
  # of the conditions above. Note that the
  # condition y == 0 is only used once and
  # therefore a logical vector is not needed
  output[is.na(x) | is.na(y)] <- NA_character_
  output[x_2 & y_2] <- 'previous_headache_maintained'
  output[x_2 & y == 0] <- 'previous_headache_ceased'
  output[x_vec & y_2] <- 'new_onset_headache'
  output[x_vec & y_vec] <- 'no_headache'
  # return to caller
  output
}

Q10_incidence <- incidence_headaches(Q10_headache_tibble[, 1], Q10_headache_tibble[, 2])
head(Q10_incidence)
#[1] "no_headache"                  "previous_headache_ceased"    
#[3] "previous_headache_maintained" "previous_headache_maintained"
#[5] "no_headache"                  "no_headache"