NA 的 R 函数问题条件的长度 > 1,并且只使用第一个元素

R function problem with NA the condition has length > 1 and only the first element will be use

我有以下功能,returns 从 ID 年龄:

giveAge = function(id){
     # start returns the place where any number starts in the id string
     start = regexpr(id, pattern ="[0-9]")[[1]] 
     # age returns the age by using the year the id was born 
     age  = ifelse(substr(id,start,start) == 0,
            lubridate::year(Sys.Date()) - (2000 + as.numeric(substr(id,start,start + 1))),
            lubridate::year(Sys.Date()) - (1900 + as.numeric(substr(id,start,start + 1)))
     )
     return(age)
}

例如,假设我们有一个包含四个 ID 的向量,但缺少第三个 ID。 (1975年AAHG,1991年FFCH,1955年CUM)

IDs = c("AAHG7511083A8", "FFCH9108017U2", NA, "CUM550117112")

IDs中使用giveAge我们得到

> giveAge(IDs)
[1] 46 30 NA 66

这里一切都很酷,但当缺失值在向量中排在第一位时

IDs2 = c(NA, "AAHG7511083A8", "FFCH9108017U2", "CUM550117112")

当应用 giveAgeIDs2 我得到

> giveAge(IDs2)
[1] NA NA NA NA

如果值为 NA,我尝试通过输入任意数字来解决问题,但我收到警告并且该函数未应用于整个向量:c

giveAge2 = function(id){
     if(!is.na(id)){
         start = regexpr(id, pattern ="[0-9]")[[1]] 
      
         age  = ifelse(substr(id,start,start) == 0,
                lubridate::year(Sys.Date()) - (2000 + as.numeric(substr(id,start,start + 1))),
                lubridate::year(Sys.Date()) - (1900 + as.numeric(substr(id,start,start + 1)))
         )
         return(age)
     } else {
         return(28)  
     }
}

> giveAge2(IDs2)
[1] 28
Warning message:
In if (!is.na(id)) { :
  the condition has length > 1 and only the first element will be used

我该如何解决这个问题?

谢谢。

在函数中,用is.na创建一个逻辑索引。然后使用索引从输入向量中提取并分配给return值。

giveAge <- function(id){
  # start returns the place where any number starts in the id string
  i_na <- is.na(id)
  age <- rep(NA_real_, length(id))
  start <- regexpr(id[!i_na], pattern ="[0-9]")[[1]] 
  # age returns the age by using the year the id was born 
  age[!i_na] <- ifelse(substr(id[!i_na],start,start) == 0,
                       lubridate::year(Sys.Date()) - (2000 + as.numeric(substr(id[!i_na],start,start + 1))),
                       lubridate::year(Sys.Date()) - (1900 + as.numeric(substr(id[!i_na],start,start + 1)))
  )
  age
}

IDs = c("AAHG7511083A8", "FFCH9108017U2", NA, "CUM550117112")
IDs2 = c(NA, "AAHG7511083A8", "FFCH9108017U2", "CUM550117112")

giveAge(IDs)
#[1] 46 30 NA 71
giveAge(IDs2)
#[1] NA 46 30 71

is.na(id) returns TRUEFALSE id 中的每个值。由于 id 在您的示例中设置为 c(NA, "AAHG7511083A8", "FFCH9108017U2", "CUM550117112"),因此 is.na(id) 的输出将为 TRUE, FALSE, FALSE; FALSE

然而,if() 函数只需要一个值(单个 TRUEFALSE)。不检查其余部分:如果问题仅出现“当缺失值在向量中排在第一位时”,您可以使用 if(!is.na(id[1]))

检查第一个值是否为 NA

1) 问题中的 giveAge 代码仅根据输入的第一个元素计算开始,因此如果该元素为 NA,则所有内容均为 NA。如果 [[1]] 被删除,问题中的 giveAge 将起作用。

(giveAge2 存在上述问题,而且它正在将向量传递给 if 语句,但此类语句需要标量。)

2) 交替尝试这个。我们还删除了对包的依赖。这会从每个字符串的左侧修剪非数字,取出剩余部分的前 2 位数字并将其转换为数字,给出 2 位数字年份 yy。然后它将其转换为给出年份的 4 位数年份并从当前年份中减去它。

giveAge3 <- function(id, today = Sys.Date(), cutoff = 10) {
  yy <- as.numeric(substr(trimws(id, "left", "\D"), 1, 2))
  year <- yy + 1900 + 100 * (yy < cutoff)
  as.numeric(format(today, "%Y")) - year
}

giveAge3(IDs)
## [1] 46 30 NA 66

giveAge3(IDs2)
## [1] NA 46 30 66