使用as.POSIXlt以微秒为单位舍入错误

时间:2013-04-24 09:09:00

标签: r datetime time posixct

我发现as.POSIXlt的一些奇怪的行为,我无法解释,我希望别人可以。在调查this question时,我发现有时一秒的小数部分会被错误地舍入

例如,下面的数字表示自纪元开始以来的特定秒数,最后6位数是第二个数字的小数部分,因此第一个数字的秒数应为.645990。

# Generate sequence of integers to represent date/times
times <- seq( 1366039619645990 , length.out = 11 )
options(scipen=20)
times
 [1] 1366039619645990 1366039619645991 1366039619645992 1366039619645993 1366039619645994 1366039619645995
 [7] 1366039619645996 1366039619645997 1366039619645998 1366039619645999 1366039619646000

# Convert to date/time with microseconds 
options(digits.secs = 6 )
as.POSIXlt( times/1e6, tz="EST", origin="1970-01-01") + 5e-7
 [1] "2013-04-15 10:26:59.645990 EST" "2013-04-15 10:26:59.645991 EST" "2013-04-15 10:26:59.645992 EST"
 [4] "2013-04-15 10:26:59.645993 EST" "2013-04-15 10:26:59.645994 EST" "2013-04-15 10:26:59.645995 EST"
 [7] "2013-04-15 10:26:59.645996 EST" "2013-04-15 10:26:59.645997 EST" "2013-04-15 10:26:59.645998 EST"
[10] "2013-04-15 10:26:59.645999 EST" "2013-04-15 10:26:59.646000 EST"

我发现我必须添加一个小的增量,等于最小时间变化的一半,以正确表示一秒的小数部分,否则会出现舍入错误。如果我在上面的数字序列上运行as.POSIXlt,它就可以正常工作了,但是如果我尝试转换一个数字,即应该以.645999结尾的那个数字,那么截断的数字为.645,我会这样做不知道为什么!

# Now just convert the date/time that should end in .645999
as.POSIXlt( times[10]/1e6, tz="EST", origin="1970-01-01") + 5e-7
[1] "2013-04-15 10:26:59.645 EST"

as.POSIXlt返回的向量中的第10个元素与上面等效的单个元素进行比较。发生了什么事?

会话信息:

R version 2.15.2 (2012-10-26)
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

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

other attached packages:
[1] raster_2.0-41 sp_1.0-5     

loaded via a namespace (and not attached):
[1] grid_2.15.2     lattice_0.20-13 tools_2.15.2

2 个答案:

答案 0 :(得分:2)

这似乎是一个舍入问题,因此丢弃了小数秒的有效数字。 违规(?)代码采用类POSIXlt对象的格式方法,即format.POSIXlt使用的print.POSIXlt

如果我们使用下面的两个值作为示例,format.POSIXlt使用下面的一行我已经包含在一个sapply中来测试小数秒的四舍五入到连续更大的位数之间的差值的绝对值。

secs <- c( 59.645998 , 59.645999 )
sapply( seq_len(np) - 1L , function(x) abs(secs - round(secs, x)) )
         [,1]     [,2]     [,3]     [,4]     [,5]     [,6]
[1,] 0.354002 0.045998 0.004002 0.000002 0.000002 0.000002
[2,] 0.354001 0.045999 0.004001 0.000001 0.000001 0.000001

正如您所看到的那样,当秒数为.xxx999时,任何四舍五入到3位或更多位数都会给出0.000001,从而影响打印:

# the number of digits used for the fractional seconds is gotten here
np <- getOption("digits.secs")

# and the length of digits to be printed is controlled in this loop
for (i in seq_len(np) - 1L) if (all(abs(secs - round(secs, 
                i)) < 0.000001)) {
                np <- i
                break
            }

这是因为在上述方法中实际发现的0.000001是:

sprintf( "%.20f" , abs(secs[2] - round(secs,5)))
[1] "0.00000099999999991773"            

# In turn this is used to control the printing of the fractional seconds            
if (np == 0L) 
            "%Y-%m-%d %H:%M:%S"
        else paste0("%Y-%m-%d %H:%M:%OS", np) 

因为在舍入中使用的测试,小数秒被截断到仅3个小数位。我想如果for循环中的测试值设置为5e-7,这个问题就会消失。

当返回的结果是POSIXlt个对象的向量时,必须调用不同的打印方法。

答案 1 :(得分:1)

我没有得到正确答案(继续研究)但我认为这很有趣:

times <- seq( 1366039619645990 , length.out = 11 )
# Convert to date/time wz="EST", origin="1970-01-01") + 5e-7
options(digits.secs = 6 )

test <- as.POSIXlt( times/1e6, tz="EST", origin="1970-01-01") + 5e-7

test1[1] <- NULL
for(i in 1:11)
  test1[i] <- as.POSIXlt(times[i]/1e6, tz="EST", origin="1970-01-01") + 5e-7

> identical(test, test1)
[1] TRUE
顺便说一句,在单一陈述中,我得到了和你一样的结果......

> test
 [1] "2013-04-15 10:26:59.645990 EST" "2013-04-15 10:26:59.645991 EST" "2013-04-15 10:26:59.645992 EST"
 [4] "2013-04-15 10:26:59.645993 EST" "2013-04-15 10:26:59.645994 EST" "2013-04-15 10:26:59.645995 EST"
 [7] "2013-04-15 10:26:59.645996 EST" "2013-04-15 10:26:59.645997 EST" "2013-04-15 10:26:59.645998 EST"
[10] "2013-04-15 10:26:59.645999 EST" "2013-04-15 10:26:59.646000 EST"
> test[10]
[1] "2013-04-15 10:26:59.645 EST"
> as.POSIXlt( times[10]/1e6, tz="EST", origin="1970-01-01") + 5e-7
[1] "2013-04-15 10:26:59.645 EST"

看看最后两个语句,似乎这个问题主要与显示单个值而不是向量有关。但即使在这种情况下,它也可能是截断,可能是floor,而不是舍入。