Cómo evitar que ifelse() convierta objetos de fecha en objetos numéricos

Resuelto Zach asked hace 54 años • 7 respuestas

Estoy usando la función ifelse()para manipular un vector de fecha. Esperaba que el resultado fuera de clase Datey me sorprendió obtener un numericvector 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 Dateobjeto.

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 Datevectores? 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 ifelseindica 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.

Zach avatar Jan 01 '70 08:01 Zach
Aceptado

Puede utilizar data.table::fifelse( data.table >= 1.12.3) o dplyr::if_else.


data.table::fifelse

A diferencia de ifelse, fifelseconserva 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.0las notas de la versión :

[ if_else] tienen una semántica más estricta que ifelse(): los argumentos truey falsedeben 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" 
Henrik avatar Jun 29 '2016 07:06 Henrik

Se relaciona con el Valor documentado de ifelse:

Un vector de la misma longitud y atributos (incluidas dimensiones y " class") que testlos valores de datos de yeso no. El modo de respuesta será forzado desde el lógico para acomodar primero los valores tomados de yesy luego los valores tomados de no.

Reducido a sus implicaciones, ifelsehace 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_elseen el complejo magrittr/dplyr/tidyr de paquetes de modelado de datos que preserva la clase del consecuente.

IRTFM avatar Jul 12 '2011 18:07 IRTFM