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-файле, и я должен найти способ конвертировать их в правильное время в миллисекундах из строки.
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 года.
Это та же проблема, что и загадка в миллисекундах при вызове 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"
В ходе тестирования я отметил, что эта проблема все еще существует для 32-битного R 3.01 и что это связано с усечением данных с плавающей запятой, характерных для 32-битной реализации операторов print, format и as.character для времени даты POSIXlt.
Базовые данные не были сохранены в другом типе, который приводит к усечению в одном случае (32 бита), а не в другом (64 бита), но функции "print", "format" и "as.character" для В частности, тип 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)))
}
Есть миллисекунды:
unclass(as.POSIXlt("13:29:56.061", '%H:%M:%OS', tz='UTC'))
$sec
[1] 56.061
...
(Здесь нет необходимости вызывать формат, это имя аргумента, а не обязательный ввод какой-либо другой функции).
В противном случае я не могу воспроизвести (в Windows 64-bit 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)
...