According to this benchmark, the fastest method to convert character dates in standard unambiguous format (YYYY-MM-DD
) into class Date
is to use as.Date(fasttime::fastPOSIXct())
.
Unfortunately, this requires to test the format beforehand because your other format DD/MM/YYYY
is misinterpreted by fasttime::fastPOSIXct()
.
So, if you don't want to bother about the format of each date column you may use the anytime::anydate()
function:
# sample data
df <- data.frame(
X1 = c("1996-01-04", "1996-01-05", "1996-01-08", "1996-01-09", "1996-01-10", "1996-01-11"),
X2 = c("02/01/1996", "03/01/1996", "04/01/1996", "05/01/1996", "08/01/1996", "09/01/1996"),
stringsAsFactors = FALSE)
library(data.table)
# convert date columns
date_cols <- c("X1", "X2")
setDT(df)[, (date_cols) := lapply(.SD, anytime::anydate), .SDcols = date_cols]
df
X1 X2
1: 1996-01-04 1996-02-01
2: 1996-01-05 1996-03-01
3: 1996-01-08 1996-04-01
4: 1996-01-09 1996-05-01
5: 1996-01-10 1996-08-01
6: 1996-01-11 1996-09-01
The benchmark timings show that there is a trade off between the convenience offered by the anytime
package and performance. So if speed is crucial, there is no other way to test the format of each column and to use the fastest conversion method available for the format.
The OP has used the try()
function for this purpose. The solution below uses regular expressions to find all columns which match a given format (only row 1 is used to save time). This has the additional benefit that the names of the relevant columns are determined automatically and need not to be typed in.
# enhanced sample data with additional columns
df <- data.frame(
X1 = c("1996-01-04", "1996-01-05", "1996-01-08", "1996-01-09", "1996-01-10", "1996-01-11"),
X2 = c("02/01/1996", "03/01/1996", "04/01/1996", "05/01/1996", "08/01/1996", "09/01/1996"),
X3 = "other data",
X4 = 1:6,
stringsAsFactors = FALSE)
library(data.table)
options(datatable.print.class = TRUE)
# coerce to data.table
setDT(df)[]
# convert date columns in standard unambiguous format YYYY-MM-DD
date_cols1 <- na.omit(names(df)[
df[1, sapply(.SD, stringr::str_detect, pattern = "\d{4}-\d{2}-\d{2}"),]])
# use fasttime package
df[, (date_cols1) := lapply(.SD, function(x) as.Date(fasttime::fastPOSIXct(x))),
.SDcols = date_cols1]
# convert date columns in DD/MM/YYYY format
date_cols2 <- na.omit(names(df)[
df[1, sapply(.SD, stringr::str_detect, pattern = "\d{2}/\d{2}/\d{4}"),]])
# use lubridate package
df[, (date_cols2) := lapply(.SD, lubridate::dmy), .SDcols = date_cols2]
df
X1 X2 X3 X4
<Date> <Date> <char> <int>
1: 1996-01-04 1996-01-02 other data 1
2: 1996-01-05 1996-01-03 other data 2
3: 1996-01-08 1996-01-04 other data 3
4: 1996-01-09 1996-01-05 other data 4
5: 1996-01-10 1996-01-08 other data 5
6: 1996-01-11 1996-01-09 other data 6
Caveat
In case one of the date columns does contain NA
in the first row, this column may escape unconverted. To handle these cases, the above code needs to be amended.