The answer provided by @ fabian-werner is great, but objects can have several classes, and the “factor” may not necessarily be the first one returned by class(yes) , so I suggest this little modification to check all the attributes of the class:
safe.ifelse <- function(cond, yes, no) { class.y <- class(yes) if ("factor" %in% class.y) {
I also sent a request to the R Development team to add a documented option so that base :: ifelse () saves attributes based on the user's choice of which attributes are saved. Request here: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16609 - It is already marked as "WONTFIX" on the grounds that it has always been the way it is now, but I introduced the next argument is why simply adding can save a lot of R users ’troubles. Perhaps your“ +1 ”in this error stream will make the R Core team take a second look.
EDIT: here is a more efficient version that allows the user to specify which attributes to save, either "cond" (default behavior ifelse ()), "yes", behavior according to the code above, or "no", for cases when no attributes are better:
safe_ifelse <- function(cond, yes, no, preserved_attributes = "yes") { # Capture the user 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 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
Mekki MacAulay Nov 23 '15 at 20:57 2015-11-23 20:57
source share