Ввод dt
-
dt <- data.frame(a_check=c(1,2,1,1,2),
b_check=c(0,1,NA,1,15),
c_check=c(1,0,0,1,NA),
d_check=c(1,1,1,0,0),
e_check=c(1,NA,0,1,1))
Списки проверки -
valid_values <- list(a_check= c(1,2,3), b_check= c(0,1),c_check=c(0,1,2),d_check="possitive integer",e_check="positive integer")
required_list <- list(a_check= 1, b_check= 1,c_check=0,d_check=1,e_check=0)
col_type_list <- list(a_check= "factor", b_check= "factor",c_check="continuous",d_check="continuous",e_check="continuous")
Вопрос-
Я пытаюсь получить ниже желаемого результата, используя несколько ifelse
условий, как показано ниже -
- Если
variable
требуется в required_list
и dt
содержит NA
для этого столбца, чем его должно дать error
(переменная не может быть NA, потому что это требуется). - Если
variable
равно continuous
в col_type_list
, то оно должно содержать только положительные значения в dt
else (переменная должна быть положительным целым числом) - Если
variable
равен factor
в col_type_list
, чем его значение должно соответствовать значению в списке valid_value
else (переменная должна быть одним из следующих значений).
Я могу получить результат, используя nested for loops
, но он неэффективен для большого набора данных.
Мой код -
param_names <- colnames(dt)
error_msg <- list()
error <- list()
for(i in 1:nrow(dt)){
for(j in 1:length(param_names))
{
if(get(param_names[j],required_list) %in% 1 & is.na(as.numeric(unlist(dt[param_names[j]]))[i]) == TRUE)
{
error_msg[j] <- paste0(toupper(param_names[j]), " cannot be NA because it is required")
}
## continuous variable check
else if(get(param_names[j],col_type_list)=="continuous"){
if (is.na(as.numeric(unlist(dt[param_names[j]]))[i]) | as.numeric(unlist(dt[param_names[j]]))[i] < 0) {
error_msg[j] <- paste0(toupper(param_names[j]), " must be a positive integer")
} else {
error_msg[j] <- NA
}
} else {
## factor variable check
if(!(as.numeric(unlist(dt[param_names[j]]))[i] %in% get(param_names[j],valid_values))){
error_msg[j] <- paste0(toupper(param_names[j]), " must be one of the following values ", paste(get(param_names[j],valid_values), collapse = '-'))
} else {
error_msg[j] <- NA
}
}
} ## end of inner for loop
error[i] <- paste(unlist(error_msg),collapse = " & ")
}## end of inner f
final_error <- unlist(error)
setDT(dt)
dt[,error := final_error]
dt[,error := gsub("NA & | NA \\s+ &", "\\1", error)]
dt[,error := gsub("& \\s+ NA | & NA", "\\1", error)]
Вывод -
> dt
a_check b_check c_check d_check e_check error
1: 1 0 1 1 1 NA
2: 2 1 0 1 NA E_CHECK must be a positive integer
3: 1 NA 0 1 0 B_CHECK cannot be NA
4: 1 1 1 0 1 NA
5: 2 15 NA 0 1 B_CHECK must be one of the following values 0-1 & C_CHECK must be a positive integer
Примечание - Я знаю, что это может быть достигнуто с помощью чего-то подобного, @ Jav
dt[, error := lapply(param_names, function(x) {
((get(x, dt) %in% get(x, valid_values))) %>%
ifelse(., " ", paste(x, "should have valid values like -", paste(get(x, valid_values), collapse = " ")))
}) %>% Reduce(paste, .)]
Но я боюсьgling использовать несколько ifelse
условие, используя вышеуказанное решение.Я ищу эффективное и чистое решение, чтобы избежать for loops
.Любой другой метод также будет работать.