A resposta fornecida por @ fabian-werner é ótima, mas os objetos podem ter várias classes e "fator" pode não ser necessariamente o primeiro retornado por class(yes)
, então sugiro esta pequena modificação para verificar todos os atributos de classe:
safe.ifelse <- function(cond, yes, no) {
class.y <- class(yes)
if ("factor" %in% class.y) { # Note the small condition change here
levels.y = levels(yes)
}
X <- ifelse(cond,yes,no)
if ("factor" %in% class.y) { # Note the small condition change here
X = as.factor(X)
levels(X) = levels.y
} else {
class(X) <- class.y
}
return(X)
}
Também enviei uma solicitação com a equipe de desenvolvimento do R para adicionar uma opção documentada para ter atributos base :: ifelse () preserve com base na seleção do usuário de quais atributos preservar. A solicitação está aqui: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16609 - Ela já foi sinalizada como "WONTFIX", alegando que sempre foi dessa maneira, mas forneci um argumento de acompanhamento sobre por que uma simples adição pode economizar muitas dores de cabeça dos usuários do R. Talvez o seu "+1" nesse segmento de bug incentive a equipe do R Core a dar uma segunda olhada.
EDIT: Aqui está uma versão melhor que permite ao usuário especificar quais atributos preservar, "cond" (comportamento padrão ifelse ()), "yes", o comportamento conforme o código acima ou "no", nos casos em que o atributos do valor "no" são melhores:
safe_ifelse <- function(cond, yes, no, preserved_attributes = "yes") {
# Capture the user's choice for which attributes to preserve in return value
preserved <- switch(EXPR = preserved_attributes, "cond" = cond,
"yes" = yes,
"no" = no);
# Preserve the desired values and check if object is a factor
preserved_class <- class(preserved);
preserved_levels <- levels(preserved);
preserved_is_factor <- "factor" %in% preserved_class;
# We have to use base::ifelse() for its vectorized properties
# If we do our own if() {} else {}, then it will only work on first variable in a list
return_obj <- ifelse(cond, yes, no);
# If the object whose attributes we want to retain is a factor
# Typecast the return object as.factor()
# Set its levels()
# Then check to see if it's also one or more classes in addition to "factor"
# If so, set the classes, which will preserve "factor" too
if (preserved_is_factor) {
return_obj <- as.factor(return_obj);
levels(return_obj) <- preserved_levels;
if (length(preserved_class) > 1) {
class(return_obj) <- preserved_class;
}
}
# In all cases we want to preserve the class of the chosen object, so set it here
else {
class(return_obj) <- preserved_class;
}
return(return_obj);
} # End safe_ifelse function
if_else()
no pacote dplyr que pode substituirifelse
, mantendo as classes corretas dos objetos Date - ela é postada abaixo como resposta recente. Estou chamando a atenção aqui, pois resolve esse problema, fornecendo uma função que é testada em unidade e documentada em um pacote CRAN, diferente de muitas outras respostas que (a partir deste comentário) foram classificadas à frente.