Die Antwort von @ fabian-werner ist großartig, aber Objekte können mehrere Klassen haben, und "Faktor" muss nicht unbedingt der erste sein, der von zurückgegeben wird. Daher empfehle class(yes)
ich diese kleine Änderung, um alle Klassenattribute zu überprüfen:
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)
}
Ich habe auch eine Anfrage an das R-Entwicklungsteam gesendet, um eine dokumentierte Option hinzuzufügen, mit der base :: ifelse () Attribute beibehalten soll, basierend auf der Benutzerauswahl, welche Attribute beibehalten werden sollen. Die Anfrage ist hier: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16609 - Sie wurde bereits als "WONTFIX" gekennzeichnet, da sie immer so war, wie sie jetzt ist. Aber ich habe ein nachfolgendes Argument geliefert, warum eine einfache Hinzufügung viele Kopfschmerzen von R-Benutzern ersparen könnte. Vielleicht ermutigt Ihr "+1" in diesem Bug-Thread das R Core-Team, einen zweiten Blick darauf zu werfen.
BEARBEITEN: Hier ist eine bessere Version, mit der der Benutzer angeben kann, welche Attribute beibehalten werden sollen, entweder "cond" (Standardverhalten von ifelse ()), "yes", das Verhalten gemäß dem obigen Code oder "no" für Fälle, in denen die Attribute des "no" -Werts sind besser:
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()
Das dplyr-Paket enthält jetzt eine Funktion, dieifelse
bei Beibehaltung der korrekten Klassen von Datumsobjekten ersetzt werden kann. Sie wird unten als aktuelle Antwort aufgeführt. Ich mache hier darauf aufmerksam, da es dieses Problem löst, indem es eine Funktion bereitstellt, die in einem CRAN-Paket getestet und dokumentiert ist, im Gegensatz zu vielen anderen Antworten, die (ab diesem Kommentar) davor eingestuft wurden.