为什么 if 循环不能在 R 中的函数中正确捕获 NA 值?

Why does an if loop not catch an NA value correctly inside a function in R?

提问人:jaggedjava 提问时间:11/3/2023 最后编辑:jaggedjava 更新时间:11/4/2023 访问量:49

问:

我有一个相当复杂的函数,可以修改一些字符变量。在对函数进行编码时,我在处理 NA 值时遇到了一个奇怪的问题。我将为您省去复杂的函数,而是在下面的 MWE 中提出问题:

# Create an example data frame
df <- data.frame(noun = c("apple", NA, "banana"))

# Display the example data frame
df
#>     noun
#> 1  apple
#> 2   <NA>
#> 3 banana

# Introduce the function 
process_my_df <- function(input_data, my_var) {
  # Create a new variable based on an existing variable
  for (i in 1:nrow(input_data)) {
    if (!is.na(input_data[[my_var]][i])) {
      input_data[[paste0(my_var, "_result")]][i] <- "is a fruit"
    }
  }
  return(input_data)
}

# Call the function to process the data frame
processed_df <- process_my_df(df, "noun")

# Display the resulting df
processed_df
#>     noun noun_result
#> 1  apple  is a fruit
#> 2   <NA>  is a fruit
#> 3 banana  is a fruit

创建于 2023-11-03 with reprex v2.0.2

我的问题:根据条件,我期望以下结果:if (!is.na(input_data[[my_var]][i])) {}

#>     noun noun_result
#> 1  apple  is a fruit
#> 2   <NA>        <NA>
#> 3 banana  is a fruit

这是怎么回事?

编辑:

由于下面公认的答案,我在函数中添加了一行简单的行,现在一切正常:

# Introduce the function 
process_my_df <- function(input_data, my_var) {
  # Create a new variable based on an existing variable
  
  # But first, "prime" it with NA_character_
  input_data[[paste0(my_var, "_result")]] = NA_character_
  
  for (i in 1:nrow(input_data)) {
    if (!is.na(input_data[[my_var]][i])) {
      input_data[[paste0(my_var, "_result")]][i] <- "is a fruit"
    }
  }
  return(input_data)
}

创建于 2023-11-03 with reprex v2.0.2

r 函数 if-语句 na

评论

2赞 jpsmith 11/3/2023
你使用循环是有原因的吗?你可以通过将整个循环替换为forinput_data[[paste0(my_var, "_result")]] <- ifelse(!is.na(input_data[my_var]), "is a fruit", NA)
3赞 MrFlick 11/3/2023
看看当你在“new_col”还不存在的地方运行时会发生什么。此新列中的其余值也需要填充,以便回收初始值。逐行创建向量通常是有问题的。这个循环可以被一个或一些矢量化的东西所取代。df[["new_col"]][1] <- "hello"ifelse
0赞 jaggedjava 11/4/2023
@MrFlick我怀疑有这样的事情,但通过你提供的简单例子,你真的确定了引擎盖下发生的事情。
1赞 jaggedjava 11/4/2023
@jpsmith实际上我自己最终使用了 ifelse() 语句,但这个最初的“回收”问题困扰着我。但现在你们已经解决了。

答:

2赞 Andrey Shabalin 11/3/2023 #1

当您隐式创建新列时,会出现此问题。如果显式执行此操作,则它可以正常工作:

# Call the function to process the data frame
df$noun_result = ""
processed_df <- process_my_df(df, "noun")

# Display the resulting df
processed_df
# noun noun_result
# 1  apple  is a fruit
# 2   <NA>            
# 3 banana  is a fruit

评论

0赞 Dean MacGregor 11/3/2023
如果默认值应为 NA 而不是空字符串,则执行此操作以启动。df$noun_result = as.character(NA)
0赞 Andrey Shabalin 11/3/2023
好吧,那么如果我们迂腐。NA_character_
1赞 Dean MacGregor 11/4/2023
TIL 这就是获得 NA 字符的方法。我一直在做上述事情,同时发现这是不对的,但从不费心去寻找正确的方法。
1赞 Marcus 11/3/2023 #2

鉴于 @Andrey Shabalin 提供的解释,您需要一个条件else

process_my_df <- function(input_data, my_var) {
  # Create a new variable based on an existing variable
  for (i in 1:nrow(input_data)) {
    if (!is.na(input_data[[my_var]][i])) {
      input_data[[paste0(my_var, "_result")]][i] <- "is a fruit"
    } else {
      input_data[[paste0(my_var, "_result")]][i] <- NA
    }
  }
  return(input_data)
}

评论

0赞 Andrey Shabalin 11/3/2023
或者只是只遍历非 NA 线路for(i in which(!is.na(input_data[[my_var]])))
0赞 jaggedjava 11/4/2023
@Andrey 这不就是用“回收”初始值来重现原来的问题吗?
0赞 jaggedjava 11/4/2023
@Marcus 谢谢你的解决方案,我自己想出了一个几乎相同的解决方案,但想知道为什么我的初始代码不起作用,因此决定将这个问题发布给 SO。