2012-06-07 109 views
5

鉴于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提供了背景信息,但并没有解决我的问题: Milliseconds puzzle when calling strptime in R

另外这个URL涉及的问题,但并没有解决它: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 

的URL也似乎表明,这只是一个显示问题,但我注意到,使用像"%OS3"语句没有选择行不似乎取得正确的位数。

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

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

+1

格式()在这里的使用是不必要和分散注意力。 。 。 – mdsumner

+0

好的,但我们需要'format ='%H:%M:%OS'。 –

+0

另请参阅http://stackoverflow.com/a/7730759/210673 – Aaron

回答

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作出的。

+0

试用32位版本。 –

+0

@AndrewStern我不能没有32位系统来试试它。我已经更新了我的答案。尝试添加一小部分(在你的情况下为0.0005)到你的时间* *之后,你将它们作为“POSIXlt”对象,看看是否改善了情况。按照该R-Devel线程获取更多细节。 –

+1

我可以重现 - 我有一个Win7 64位系统上安装32位和64位R。看起来问题是特定于32位R. – Fhnuzoag

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) 
... 
+0

当我使用下面的方法对它进行匿名化时,似乎确实是正确的: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位版本可能会更准确,因为寄存器更大。 –

+1

必须是错误的屏幕。 – mdsumner

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(x, "%H:%M:%OS6") 
[1] "13:29:56.060999" 
1

在测试中我已经指出,这个问题仍然存在32位[R 3.01,并认为这是由于浮点数据的截断特定于32位实现的:你可以通过调用format直接看到用于POSIXlt日期时间的打印,格式和as.character操作符。

底层数据尚未存储在导致截断的一种情况下(32位)而不是另一种(64位),但“打印”,“格式”和“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 

我认为,这是一个应该被固定在底层基础库中的缺陷,作为一个临时的解决办法不过,您可以覆盖“打印”,“as.character”和“格式”功能以将输出更改为所需的输出,例如

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))) 
    }