Cómo evitar que ifelse() convierta objetos de fecha en objetos numéricos
Estoy usando la función ifelse()
para manipular un vector de fecha. Esperaba que el resultado fuera de clase Date
y me sorprendió obtener un numeric
vector en su lugar. Aquí hay un ejemplo:
dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05'))
dates <- ifelse(dates == '2011-01-01', dates - 1, dates)
str(dates)
Esto es especialmente sorprendente porque al realizar la operación en todo el vector se devuelve un Date
objeto.
dates <- as.Date(c('2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04','2011-01-05'))
dates <- dates - 1
str(dates)
¿Debería utilizar alguna otra función para operar con Date
vectores? Si es así, ¿qué función? Si no, ¿cómo lo fuerzo?ifelse
la devolución de un vector del mismo tipo que la entrada?
La página de ayuda ifelse
indica que se trata de una característica, no de un error, pero todavía estoy luchando por encontrar una explicación para lo que me pareció un comportamiento sorprendente.
Puede utilizar data.table::fifelse
( data.table >= 1.12.3
) o dplyr::if_else
.
data.table::fifelse
A diferencia de
ifelse
,fifelse
conserva el tipo y clase de las entradas.
library(data.table)
dates <- fifelse(dates == '2011-01-01', dates - 1, dates)
str(dates)
# Date[1:5], format: "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
dplyr::if_else
De dplyr 0.5.0
las notas de la versión :
[
if_else
] tienen una semántica más estricta queifelse()
: los argumentostrue
yfalse
deben ser del mismo tipo. Esto proporciona un tipo de retorno menos sorprendente y conserva los vectores S3 como fechas ".
library(dplyr)
dates <- if_else(dates == '2011-01-01', dates - 1, dates)
str(dates)
# Date[1:5], format: "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
Se relaciona con el Valor documentado de ifelse
:
Un vector de la misma longitud y atributos (incluidas dimensiones y "
class
") quetest
los valores de datos deyes
ono
. El modo de respuesta será forzado desde el lógico para acomodar primero los valores tomados deyes
y luego los valores tomados deno
.
Reducido a sus implicaciones, ifelse
hace que los factores pierdan sus niveles y las Fechas pierdan su clase y sólo se restablezca su modo ("numérico"). Pruebe esto en su lugar:
dates[dates == '2011-01-01'] <- dates[dates == '2011-01-01'] - 1
str(dates)
# Date[1:5], format: "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
Podrías crear un safe.ifelse
:
safe.ifelse <- function(cond, yes, no){ class.y <- class(yes)
X <- ifelse(cond, yes, no)
class(X) <- class.y; return(X)}
safe.ifelse(dates == '2011-01-01', dates - 1, dates)
# [1] "2010-12-31" "2011-01-02" "2011-01-03" "2011-01-04" "2011-01-05"
Una nota posterior: veo que Hadley ha integrado if_else
en el complejo magrittr/dplyr/tidyr de paquetes de modelado de datos que preserva la clase del consecuente.