我有一个数据框:

mydf <- data.frame(
  col1 = c("54", "abc", "123", "54 abc", "zzz", "a", "99"),
  col2 = c("100", "200", "300", "400", "500", "600", "700"),
  stringsAsFactors = FALSE
)

在此数据框中,我想用 NA 替换所有元素,除非它们满足以下条件之一:

  • 严格来说是一个数字(例如“54”保留,“54 abc”丢弃)
  • 属于 target_string

我不确定如何使用 apply 在 R 中执行此操作,因此我尝试编写一个循环:

target_string <- c("a", "zzz")

replace_with_na_old <- function(df, target_string) {
  for (i in 1:nrow(df)) {
    for (j in 1:ncol(df)) {
      value <- df[i, j]
      if (!grepl("^[0-9]+$", value) && !(value %in% target_string)) {
        df[i, j] <- NA
      }
    }
  }
  return(df)
}

mydf_cleaned_old <- replace_with_na_old(mydf, target_string)

还有其他方法可以做到这一点吗?

注意:以下是如何用 %like% 替换 %in%:

   replace_with_na_new <- function(df, target_string) {
  for (i in 1:nrow(df)) {
    for (j in 1:ncol(df)) {
      value <- df[i, j]
      if (!grepl("^[0-9]+$", value) && !any(sapply(target_string, function(pattern) grepl(pattern, value)))) {
        df[i, j] <- NA
      }
    }
  }
  return(df)
}


最佳答案
3

您已经有了检查这一点所需的逻辑,您所需要做的就是对其进行矢量化。

replace_with_na <- function(value, target_string) {
  value[!(grepl('^\\d+$', value) | value %in% target_string)] <- NA
  value
}

apply*现在,您可以使用基础 R 中的任何函数将此函数应用于每一列。

new_df <- mydf
new_df[] <- lapply(mydf, replace_with_na, target_string)
new_df

#  col1 col2
#1   54  100
#2 <NA>  200
#3  123  300
#4 <NA>  400
#5  zzz  500
#6    a  600
#7   99  700

或者如果您愿意,dplyr我们可以使用across类似的结果。

library(dplyr)
mydf %>% mutate(across(everything(), \(x) replace_with_na(x, target_string)))

您可以替换所有不属于target_string且包含非数字字符的元素。

mydf[sapply(mydf, \(x) grepl("\\D", x) & !x %in% target_string)] = NA

 col1 col2
1   54  100
2 <NA>  200
3  123  300
4 <NA>  400
5  zzz  500
6    a  600
7   99  700

1

  • 一个变体可能是is.na(mydf) <- vapply(mydf, \(x) grepl("\\D", x) & !x %in% target_string, logical(nrow(mydf)))


    – 

您可以提前生成正则表达式模式,然后应用grepl,例如,

patt <- sprintf(
  "^\\d+$|%s",
  paste0(sprintf("\\b%s\\b", target_string), collapse = "|")
)
list2DF(lapply(mydf, \(x) replace(x, !grepl(patt, x), NA)))

给出

  col1 col2
1   54  100
2 <NA>  200
3  123  300
4 <NA>  400
5  zzz  500
6    a  600
7   99  700

2

  • target_string在给定的例子中,这是可行的,但是当包含正则表达式将解释的内容时,可能会出现问题.。例如,也许还可以使用stringr::str_escape


    – 

  • @GKi 感谢指出这一点!是的,如果处理类似这样的问题,我的代码就会遇到麻烦.。在这种情况下,我承认其他两个比我的更强大、更安全。


    –