У меня есть фрейм данных:
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, если они не соответствуют одному из этих условий:
Я не был уверен, как это сделать в R с помощью apply, поэтому попробовал написать цикл:
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)
Есть ли другой способ сделать это?
Примечание. Вот как можно заменить %in% на %like%:
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)
}
У вас уже есть необходимая логика, чтобы это проверить, все, что вам нужно, это векторизовать ее.
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
Вы можете заранее сгенерировать шаблон регулярного выражения, а затем применить 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