R проблема с округлением миллисекунд

Учитывая следующую проблему с округлением миллисекунд в R. Как мне обойти это, чтобы время было правильным?

> options(digits.secs=3)
> as.POSIXlt("13:29:56.061", format='%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.060 UTC"
> as.POSIXlt("13:29:56.062", format='%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.061 UTC"
> as.POSIXlt("13:29:56.063", format='%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.063 UTC"

Я заметил, что этот URL-адрес предоставляет справочную информацию, но не решает мою проблему: при вызове strptime в R.

Также этот URL затрагивает проблему, но не решает ее: R xts: .001 миллисекунда в индексе.

В этих случаях я вижу следующее:

> x <- as.POSIXlt("13:29:56.061", format='%H:%M:%OS', tz='UTC')
> print(as.numeric(x), digits=20)
[1] 1339075796.0610001087

URL-адрес также, кажется, указывает на то, что это просто проблема с отображением, но я заметил, что использование таких операторов, как "%OS3" без строки параметров, похоже, не отображает правильное количество цифр.

Я использую 32-разрядную версию 2.15.0 под Windows, но, похоже, она существует и в других ситуациях для R.

Обратите внимание, что мои исходные данные - это строки даты и времени в файле CSV. Я должен найти способ преобразовать их в правильное миллисекундное время из строки.


person Andrew Stern    schedule 07.06.2012    source источник
comment
Использование format() здесь не нужно и отвлекает. . .   -  person mdsumner    schedule 07.06.2012
comment
Ну да, но нам нужно format = '%H:%M:%OS'.   -  person Gavin Simpson    schedule 07.06.2012
comment
См. также stackoverflow.com/a/7730759/210673.   -  person Aaron left Stack Overflow    schedule 07.06.2012
comment
print(56.061, digits=20) #[1] 56.060999999999999943; Вы можете добавить 0,0001 секунды ко всем своим временам и получить исправленное усечение. Точность чисел с плавающей запятой должна быть одинаковой на 32- и 64-битных машинах.   -  person IRTFM    schedule 07.06.2012


Ответы (4)


Я не вижу этого:

> options(digits.secs = 4)
> as.POSIXlt("13:29:56.061", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.061 UTC"
> as.POSIXlt("13:29:56.062", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.062 UTC"
> as.POSIXlt("13:29:56.063", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.063 UTC"
> options(digits.secs = 3)
> as.POSIXlt("13:29:56.061", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.061 UTC"
> as.POSIXlt("13:29:56.062", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.062 UTC"
> as.POSIXlt("13:29:56.063", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.063 UTC"

с участием

> sessionInfo()
R version 2.15.0 Patched (2012-04-14 r59019)
Platform: x86_64-unknown-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_GB.utf8       LC_NUMERIC=C             
 [3] LC_TIME=en_GB.utf8        LC_COLLATE=en_GB.utf8    
 [5] LC_MONETARY=en_GB.utf8    LC_MESSAGES=en_GB.utf8   
 [7] LC_PAPER=C                LC_NAME=C                
 [9] LC_ADDRESS=C              LC_TELEPHONE=C           
[11] LC_MEASUREMENT=en_GB.utf8 LC_IDENTIFICATION=C      

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

Со строками формата "%OSn" принудительно усекается. Если дробная секунда не может быть представлена ​​точно в числах с плавающей запятой, то усечение вполне может пойти неправильным путем. Если вы видите, что что-то идет не так, вы также можете явно округлить до нужной единицы или добавить половину дроби, с которой вы хотите работать (в случае, показанном 0.0005):

> t1 <- as.POSIXlt("13:29:56.061", format = '%H:%M:%OS', tz='UTC')
> t1
[1] "2012-06-07 13:29:56.061 UTC"
> t1 + 0.0005
[1] "2012-06-07 13:29:56.061 UTC"

(но я сказал, что не вижу здесь проблемы.)

Последнее замечание было сделано Саймоном Урбанеком в списке рассылки R-Devel. 30 мая 2012 г..

person Gavin Simpson    schedule 07.06.2012
comment
Попробуйте с 32-битной версией. - person Andrew Stern; 07.06.2012
comment
@AndrewStern Я не могу, у меня нет 32-битной системы, чтобы попробовать. Я обновил свой ответ. Попробуйте добавить небольшую долю (в вашем случае 0,0005) к времени после того, как вы получили их как "POSIXlt" объекты, и посмотрите, улучшит ли это ситуацию. Следите за этой веткой R-Devel для получения более подробной информации. - person Gavin Simpson; 07.06.2012
comment
Я могу воспроизвести - у меня установлены как 32-битная, так и 64-битная R в 64-битной системе Win7. Кажется, проблема специфична для 32-битного R. - person Fhnuzoag; 07.06.2012
comment
@Fhnuzoag Хорошо, значит, это проблема с плавающей запятой. Эндрю должен попробовать добавить малую часть к времени, которое усекается. - person Gavin Simpson; 07.06.2012
comment
Добавление 0,0005 к моему вектору перед его печатью, похоже, помогло. - person Andrew Stern; 07.06.2012

Это та же проблема, что и миллисекундная головоломка при вызове strptime в R.

Ваш пример:

> x <- as.POSIXlt("13:29:56.061", format='%H:%M:%OS', tz='UTC')
> print(as.numeric(x), digits=20)
[1] 1339075796.0610001087

не является репрезентативным для проблемы. as.numeric(x) преобразует ваш объект POSIXlt в POSIXct перед преобразованием в числовой, поэтому вы получаете разные ошибки округления с точностью до плавающей запятой.

Это не то, как работает print.POSIXlt (который вызывает format.POSIXlt). format.POSIXlt форматирует каждый элемент конструкции списка POSIXlt индивидуально, поэтому вам нужно будет посмотреть:

print(x$sec, digits=20)
[1] 56.060999999999999943

И это число усекается до третьего знака после запятой, так что вы видите 56.060. Вы можете увидеть это, вызвав format напрямую:

> format(x, "%H:%M:%OS6")
[1] "13:29:56.060999"
person Joshua Ulrich    schedule 07.06.2012

При тестировании я заметил, что эта проблема все еще существует для 32-разрядной версии R 3.01 и что это связано с усечением данных с плавающей запятой, которое характерно для 32-разрядной реализации операторов печати, формата и as.character для даты и времени POSIXlt.

Базовые данные не были сохранены в другом типе, который приводит к усечению в одном случае (32 бита), а не в другом (64 бита), но функции «печать», «формат» и «как.символ» для Тип POSIXlt, который используется для отображения данных POSIXlt в виде отображаемой строки.

Хотя задокументированное поведение заключается в том, что эти функции усекают (игнорируют) лишние цифры (как упоминал @Gavin Simpson), это не так для 32- и 64-битных версий. Показывать; мы сгенерируем 1000 разных значений времени и выполним несколько операций сравнения:

> options(digits.sec=3)
> x = as.POSIXlt("13:29:56.061", format='%H:%M:%OS', tz='UTC')

> for (i in 0:999) {
>     x[i+1] = as.POSIXlt(paste0("13:29:56.",sprintf("%03d",i)),format='%H:%M:%OS',tz='UTC')
> }

> sum(x[2:1000]>x[1:999])
[1] 999

Как в 32-битной, так и в 64-битной версии операторы сравнения одинаковы, однако в 32-битной я вижу:

> x[1:6]
[1] "2015-10-16 13:29:56.000 UTC" "2015-10-16 13:29:56.000 UTC"
[3] "2015-10-16 13:29:56.002 UTC" "2015-10-16 13:29:56.003 UTC"
[5] "2015-10-16 13:29:56.003 UTC" "2015-10-16 13:29:56.005 UTC"

Так что проблема явно в дисплее. Глядя на фактические числа в типе данных POSIXlt, особенно на секунды, мы видим, что происходит:

> y = (x[1:6]$sec) 
> y
[1] 56.000 56.001 56.002 56.003 56.004 56.005
> trunc(y*1000)/1000
[1] 56.000 56.001 56.002 56.003 56.004 56.005
> trunc((y-floor(y))*1000)/1000
[1] 0.000 0.000 0.002 0.003 0.003 0.005

Я бы предположил, что это ошибка, которую следует исправить в базовой базовой библиотеке, хотя в качестве временного исправления вы можете перезаписать функции «print», «as.character» и «format», чтобы изменить вывод на желаемый результат. например

format.POSIXlt = function(posix) {
    return(paste0(posix$year+1900,"-",sprintf("%02d",posix$mon+1),"-",sprintf("%02d",posix$mday)," ",
        sprintf("%02d",posix$hour),":",sprintf("%02d",posix$min),":",sprintf("%002.003f",posix$sec)))
    }

print.POSIXlt = function(posix) {
    print(paste0(posix$year+1900,"-",sprintf("%02d",posix$mon+1),"-",sprintf("%02d",posix$mday)," ",
        sprintf("%02d",posix$hour),":",sprintf("%02d",posix$min),":",sprintf("%002.003f",posix$sec)))
    }

as.character.POSIXlt = function(posix) {
    return(paste0(posix$year+1900,"-",sprintf("%02d",posix$mon+1),"-",sprintf("%02d",posix$mday)," ",
        sprintf("%02d",posix$hour),":",sprintf("%02d",posix$min),":",sprintf("%002.003f",posix$sec)))
    }
person SuaveIncompetence    schedule 16.10.2015

Миллисекунды есть:

 unclass(as.POSIXlt("13:29:56.061", '%H:%M:%OS', tz='UTC'))
 $sec
 [1] 56.061
 ...

(Здесь нет необходимости вызывать формат, это имя аргумента, а не требуемый ввод какой-либо другой функции).

В противном случае я не могу воспроизвести (в 64-разрядной версии Windows R 2.15.0):

options(digits.secs = 3)
as.POSIXlt("13:29:56.061", '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.061 UTC"

sessionInfo()
R version 2.15.0 Patched (2012-05-05 r59321)
Platform: x86_64-pc-mingw32/x64 (64-bit)
...
person mdsumner    schedule 07.06.2012
comment
Это кажется правильным, когда я расклассифицирую его, используя следующее: unclass(as.POSIXlt(13:29:56.061, %H:%M:%OS, tz='UTC')) но на экране по-прежнему отображаются неправильные миллисекунды при отображении с использованием: as.POSIXlt(13:29:56.061, %H:%M:%OS, tz='UTC') . Обратите внимание, что я использую 32-битную версию, а 64-битная версия может быть более точной, поскольку регистры больше. - person Andrew Stern; 07.06.2012