Точное преобразование из символа->POSIXct-> символа с датами с точностью до миллисекунды

У меня есть символьный столбец даты и времени в файле. Я загружаю файл (в data.table) и делать вещи, которые требуют преобразования столбца в POSIXct, Затем мне нужно написать POSIXct значение возвращается в файл, но дата и время не будут одинаковыми (потому что они напечатаны неправильно).

Эта проблема печати / форматирования хорошо известна и обсуждалась несколько раз. Я прочитал несколько постов, описывающих эту проблему. Наиболее авторитетные ответы, которые я нашел, даны в ответ на этот вопрос. Ответы на этот вопрос обеспечивают две функции (myformat.POSIXct а также form), которые должны решить эту проблему, но они, похоже, не работают на этом примере:

x <- "04-Jan-2013 17:22:08.139"
options("digits.secs"=6)
form(as.POSIXct(x,format="%d-%b-%Y %H:%M:%OS"),format="%d-%b-%Y %H:%M:%OS3")
[1] "04-Jan-2013 17:22:08.138"
form(as.POSIXct(x,format="%d-%b-%Y %H:%M:%OS"),format="%d-%b-%Y %H:%M:%OS4")
[1] "04-Jan-2013 17:22:08.1390"
myformat.POSIXct(as.POSIXct(x,format="%d-%b-%Y %H:%M:%OS"),digits=3)
[1] "2013-01-04 17:22:08.138"
myformat.POSIXct(as.POSIXct(x,format="%d-%b-%Y %H:%M:%OS"),digits=4)
[1] "2013-01-04 17:22:08.1390"

мой sessionInfo:

R version 2.15.2 (2012-10-26)
Platform: x86_64-w64-mingw32/x64 (64-bit)

locale:
[1] LC_COLLATE=English_United Kingdom.1252  LC_CTYPE=English_United Kingdom.1252
[3] LC_MONETARY=English_United Kingdom.1252 LC_NUMERIC=C                        
[5] LC_TIME=C                              

attached base packages:
[1] stats     graphics  grDevices datasets  utils     methods   base     

other attached packages:
[1] fasttime_1.0-0   data.table_1.8.9 bit64_0.9-2      bit_1.1-9
[5] sas7bdat_0.3     chron_2.3-43     vimcom_0.9-6    

loaded via a namespace (and not attached):
[1] tools_2.15.2

4 ответа

Решение

Две вещи:

1) @statquant прав (а другие известные эксперты @Joshua Ulrich и @Dirk Eddelbuettel не правы), и @Aaron в своем комментарии, но это не будет важно для основного вопроса:

POSIXlt по дизайну определенно более точен в хранении, чем POSIXct: Поскольку его секунды всегда находятся в [0, 60), он имеет гранулярность около 6e-15, т. Е. 6 фемтосекунд, что будет в десятки миллионов раз меньше гранулярности, чем POSIXct,

Однако это не очень актуально здесь (и для текущего R): почти все операции, особенно числовые, используют Ops групповой метод (да, не известен новичкам, но хорошо документирован), просто посмотрите на Ops.POSIXt который действительно разрушает дополнительную точность, сначала принуждая к POSIXct, Кроме того, формат ()/print() использует 6 знаков после "." самое большее, и, следовательно, также не различает внутренне более высокую точность POSIXlt и "только" 100 наносекундная гранулярность POSIXct,
(По вышеуказанной причине и Дирк, и Джошуа были приведены к неверному утверждению: для всех простых практических целей точность *lt и *ct одинакова).

2) Я склонен согласиться с тем, что мы (R Core) должны улучшить format() и, следовательно, print() такие доли секунд для объектов POSIXt (все еще после исправления ошибки, упомянутого @Aaron выше).
Но тогда я могу ошибаться, и "мы" поняли это правильно, по определению "правильно";-)

Поэтому, я думаю, вам нужно добавить немного фактора выдумки к моему предложению здесь: /questions/30081223/kak-r-formatiruet-posixct-s-dolyami-sekundyi/30081229#30081229. Кажется, это работает, но, возможно, может включать в себя другие ошибки; тщательно проверьте и подумайте, что он делает, прежде чем использовать что-либо важное.

myformat.POSIXct <- function(x, digits=0) {
  x2 <- round(unclass(x), digits)
  attributes(x2) <- attributes(x)
  x <- as.POSIXlt(x2)
  x$sec <- round(x$sec, digits) + 10^(-digits-1)
  format.POSIXlt(x, paste("%Y-%m-%d %H:%M:%OS",digits,sep=""))
}

Как уже говорилось в ответах на вопросы, на которые вы ссылались, то, как печатается / форматируется значение, отличается от фактического значения. Это просто печатная версия.

R> as.POSIXct('2011-10-11 07:49:36.3')-as.POSIXlt('2011-10-11 07:49:36.3')
Time difference of 0 secs
R> as.POSIXct('2011-10-11 07:49:36.2')-as.POSIXlt('2011-10-11 07:49:36.3')
Time difference of -0.0999999 secs

Ваше понимание того, что POSIXct менее точен, чем POSIXlt это неверно. Вы также неправильно говорите, что не можете включить POSIXlt Объект как столбец в data.frame.

R> x <- data.frame(date=Sys.time())
R> x$date <- as.POSIXlt(x$date)
R> str(x)
'data.frame':   1 obs. of  1 variable:
 $ date: POSIXlt, format: "2013-03-13 07:38:48"

Когда ты пишешь

Насколько я понимаю, представление POSIXct менее точно, чем представление POSIXlt

Вы совершенно не правы.

Это одинаковое представление для обоих - вплоть до миллисекунд в Windows и вплоть до (почти) микросекунд в других ОС. Вы читали help(DateTimeClasses)?

Что касается вашего последнего вопроса, то да, версия для разработки моего пакета RcppBDT использует Boost Date.Time и может идти до наносекунд, если ваша ОС поддерживает это, и вы включили правильное представление. Но он заменяет POSIXct и еще не поддерживает векторы объектов времени.

Изменить: Относительно вашего последующего вопроса:

R> one <- Sys.time(); two <- Sys.time(); two - one
Time difference of 7.43866e-05 secs
R>
R> as.POSIXlt(two) - as.POSIXlt(one)
Time difference of 7.43866e-05 secs
R> 
R> one    # options("digits.sec"=6) on my box
[1] "2013-03-13 07:30:57.757937 CDT"
R> 

Редактировать 2: я думаю, что вы просто испытываете, что представление с плавающей запятой на компьютерах неточно:

R> print(as.numeric(as.POSIXct("04-Jan-2013 17:22:08.138",
+                   format="%d-%b-%Y %H:%M:%OS")), digits=18)
[1] 1357341728.13800001
R> print(as.numeric(as.POSIXct("04-Jan-2013 17:22:08.139",
+                   format="%d-%b-%Y %H:%M:%OS")), digits=18)
[1] 1357341728.13899994
R> 

Разница не точно 1/1000, как вы предполагали.

Другие вопросы по тегам