R问题与舍入毫秒

时间:2012-06-07 12:40:03

标签: r time rounding milliseconds rounding-error

考虑到以下问题,在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"

我注意到此网址提供了背景信息但未解决我的问题: Milliseconds puzzle when calling strptime in R

此网址也涉及此问题,但未解决此问题:R xts: .001 millisecond in index

在这些情况下,我确实看到以下内容:

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

网址似乎也表明这只是一个显示问题,但我注意到使用"%OS3"这样没有选项行的语句似乎没有提取正确的位数。

我在Windows下使用的版本是32位2.15.0,但这似乎存在于R的其他情况下。

请注意,我的原始数据是CSV文件中的这些日期时间字符串我必须找到一种方法,将它们从字符串转换为正确的毫秒时间。

4 个答案:

答案 0 :(得分:5)

我没有看到:

> 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"

(但我说,我在这里看不到问题。)

后一点是Simon Urbanek on the R-Devel mailing list on 30-May-2012

答案 1 :(得分:3)

这与Milliseconds puzzle when calling strptime in 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"

答案 2 :(得分:2)

在测试中我注意到这个问题仍然存在于32位R 3.01中,这是由于浮点数据的截断是特定于POSIXlt日期时间的打印,格式和as.character运算符的32位实现。

基础数据尚未存储在导致在一种情况下(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)))
    }

答案 3 :(得分:1)

毫秒是:

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

(这里不需要调用格式,它是参数的名称,而不是其他函数所需的输入)。

否则,我无法重现(在Windows 64位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)
...