You may use data.table::fifelse (data.table >= 1.12.3) or dplyr::if_else.
data.table::fifelseUnlike
ifelse,fifelsepreserves the type and class of the inputs.
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_elseFrom dplyr 0.5.0 release notes:
[
if_else] have stricter semantics thatifelse(): thetrueandfalsearguments must be the same type. This gives a less surprising return type, and preserves S3 vectors like dates" .
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" 
    It relates to the documented Value of ifelse:
A vector of the same length and attributes (including dimensions and "
class") astestand data values from the values ofyesorno. The mode of the answer will be coerced from logical to accommodate first any values taken fromyesand then any values taken fromno.
Boiled down to its implications, ifelse makes factors lose their levels and Dates lose their class and only their mode ("numeric") is restored. Try this instead:
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"
You could create a 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"
A later note: I see that Hadley has built an if_else into the the magrittr/dplyr/tidyr complex of data-shaping packages.
DWin's explanation is spot on. I fiddled and fought with this for a while before I realized I could simply force the class after the ifelse statement:
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)
class(dates)<- "Date"
str(dates)
At first this felt a little "hackish" to me. But now I just think of it as a small price to pay for the performance returns that I get from ifelse(). Plus it's still a lot more concise than a loop.
The reason why this won't work is because, ifelse() function converts the values to factors. A nice workaround would be to convert it to characters before evaluating it.
dates <- as.Date(c('2011-01-01','2011-01-02','2011-01-03','2011-01-04','2011-01-05'))
dates_new <- dates - 1
dates <- as.Date(ifelse(dates =='2011-01-01',as.character(dates_new),as.character(dates)))
This wouldn't require any library apart from base R.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With