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

问题描述

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

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

我创建了一个函数,该函数应该返回一个与 nrow(df_headache_tibble) 长度相同的字符向量 (Q10_incidence),它基于应应用于数据帧的嵌套条件,逐行。 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,776)){
                        output<-'no_headache'
                }
        }
}

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

当我打电话

mapply(incidence_headaches,2])

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

好像跟向量化有关系,可以用嵌套的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,776)){
      output<-'no_headache'
    }
  }
}

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

apply(df_headache_tibble,MARGIN = 1,incidence_headaches)

要得到这样的东西:

> apply(df_headache_tibble,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 显然是一个完美的工作函数,没有理由不使用它。您的问题是:小标题是 data.frames,但它们的行为不像 data.frames。这很有效:

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

当你只从 data.frame 中提取一行时,它会给你一个向量,当你只从一个 tibble 中提取一行时,它会给你一个 tibble。哈德利对事情应该如何运作与发明 R data.frame 的人有不同的看法。有很多方法可以解决这个问题,例如

mapply(incidence_headaches,df_headache_tibble[,1,drop = TRUE],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,776)
  # conditions for 'y'
  y_2 <- y == 2
  y_vec <- y %in% c(0,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[,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"