Я делаю манипуляции с датой и временем и испытываю объяснимые, но неприятные проблемы с круговым отключением при преобразовании даты → времени → даты. Я временно преодолел эту проблему путем округления в соответствующих точках, но мне интересно, есть ли лучшие практики для обработки данных, которые были бы более чистыми. Я использую сочетание функций base-R и lubridate
.
tl; dr есть хороший, простой способ конвертировать из десятичной даты (YYYY.fff) в класс Date
(и обратно) без прохождения POSIXt и выполнения округления ( и потенциально часовых поясах)
Начните с нескольких дней с 1918 года в качестве отдельных столбцов год/месяц/день (не критическая часть моей проблемы, но там, где мой конвейер запускается):
library(lubridate)
dd <- data.frame(year=1918,month=9,day=1:12)
Преобразовать год/месяц/день → дата → время:
dd <- transform(dd,
time=decimal_date(make_date(year, month, day)))
Последовательные различия в результирующем векторе времени не являются точно 1 из-за округления: это понятно, но приводит к проблемам в будущем.
table(diff(dd$time)*365)
## 0.999999999985448 1.00000000006844
## 9 2
Теперь предположим, что я возвращаюсь к дате: даты немного до или после полуночи (выкл. < 1 секунда в любом направлении):
d2 <- lubridate::date_decimal(dd$time)
# [1] "1918-09-01 00:00:00 UTC" "1918-09-02 00:00:00 UTC"
# [3] "1918-09-03 00:00:00 UTC" "1918-09-03 23:59:59 UTC"
# [5] "1918-09-04 23:59:59 UTC" "1918-09-05 23:59:59 UTC"
# [7] "1918-09-07 00:00:00 UTC" "1918-09-08 00:00:00 UTC"
# [9] "1918-09-09 00:00:00 UTC" "1918-09-09 23:59:59 UTC"
# [11] "1918-09-10 23:59:59 UTC" "1918-09-12 00:00:00 UTC"
Если мне сейчас нужны даты (а не объекты POSIXct), я могу использовать as.Date()
, но, к моему сожалению, as.Date() усекает, а не округляет...
tt <- as.Date(d2)
## [1] "1918-09-01" "1918-09-02" "1918-09-03" "1918-09-03" "1918-09-04"
## [6] "1918-09-05" "1918-09-07" "1918-09-08" "1918-09-09" "1918-09-09"
##[11] "1918-09-10" "1918-09-12"
Итак, теперь разница составляет 0/1/2 дня:
table(diff(tt))
# 0 1 2
# 2 7 2
Я могу исправить это, округляя сначала:
table(diff(as.Date(round(d2))))
## 1
## 11
но мне интересно, есть ли лучший способ (например, сохранение POSIXct из моего конвейера и пребывание с датами...
Как было предложено этой статьей справочника R-справочника от 2004 года Гротендиком и Петцольдтом:
При рассмотрении того, какой класс использовать, всегда выберите наименее сложный класс, который будет поддерживать выражение. То есть используйте
Date
, если это возможно, в противном случае используйтеchron
и в противном случае используйте классыPOSIX
. Такая стратегия значительно снизит вероятность ошибки и повысит надежность вашего приложения.
В обширной таблице в этой статье показано, как перевести между Date
, chron
и POSIXct
, но не включает десятичное время в качестве одного из кандидатов...